Drupal 8 - Get All Nodes With a Part of Url Alias - drupal

I try to find a way to get all nodes by a part of the url alias in the nodes. I know i get a specific node by the complete url alias. For example:
$path = \Drupal::service('path.alias_manager')->getPathByAlias('/this-is-the-alias');
if(preg_match('/node\/(\d+)/', $path, $matches)) {
$node = \Drupal\node\Entity\Node::load($matches[1]);
}
And I know I can find multiple nodes by entity query:
$nids = \Drupal::entityQuery('node')
->condition('type','page')
->condition('status',1)
->sort('created', 'DESC')
->range(0, 20)
->execute();
But I want to know how I can implement an additional condition to filter nodes by a part of the URL alias for example "/news/*". I need all nodes with this url fragment.

Can try this-
Reference https://www.jonkamke.com/blog/2019/5/load-a-node-via-url-alias
<?php
/** #var Drupal\Core\Url $url */
$url = $variables['url'];
if ($url instanceof Drupal\Core\Url) {
$nid = $url->getRouteParameters()['node'];
/** #var \Drupal\node\Entity\Node $node */
$node = Node::load($nid);
if ($node instanceof Node) {
$type = $node->getType();
}
}

$input = '/this-is-the-alias';
$node_query_result = [];
$path_query = \Drupal::database()->select('path_alias', 'a');
$path_query->addField('a', 'path');
$path_query->condition('a.alias', '%' . $input . '%', 'LIKE');
$path_query_result = str_replace('/node/', '', $path_query->execute()->fetchCol());
if ($path_query_result) {
$node_query = \Drupal::database()->select('node_field_data', 'n');
$node_query->addField('n', 'nid');
$node_query->addField('n', 'type');
$node_query->addField('n', 'title');
$node_query->condition('n.status', NodeInterface::PUBLISHED);
$node_query->condition('nid', $path_query_result, 'IN');
$node_query_result = $node_query->execute()->fetchAll(\PDO::FETCH_ASSOC);
}
return $node_query_result;

Related

Doctrine Symfony Finding if it's in database with an array

I'm a student and, for my final project, I need to make a searchtool.
I know how to search if a string is contained in my database but I want to search with an array now. So I do it and that's work... but only if it's exactly what it's in my array, not if it's contained.
So my Repository function is like this
public function findByMots($value)
{
return $this->createQueryBuilder('a')
->andWhere('a.titre IN (:val) OR a.contenu IN (:val) OR t.theme IN (:val)')
->setParameter('val', $value)
->leftJoin('App\Entity\Theme', 't', 'WITH', 't.id_article = a.id')
->orderBy('a.id', 'DESC')
->getQuery()
->getResult()
;
}
My value is an array with string in, but if one string is "raccoon" he doesn't find my article who has a title like "I love raccoon". Does anybody know please?
I found a method!
public function findByMots($value)
{
$search = $this->createQueryBuilder('a')
->leftJoin('App\Entity\Theme', 't', 'WITH', 't.id_article = a.id');
$number = 0;
foreach ($value as $valu) {
$search = $search->orWhere('a.titre LIKE :val' . $number . ' OR a.contenu LIKE :val' . $number . ' OR t.theme LIKE :val' . $number . '')
->setParameter('val'.$number, '%'.$valu.'%');
$number++;
}
$search = $search
->orderBy('a.id', 'DESC')
->getQuery()
->getResult();
return $search;
}

Symfony 3 : how to reordering an array collection with route action?

I have a Product Entity. Each product can have 0 or N pictures.
I have a ProductPhoto Entity which has a order property (0 = first).
On a page, I list all the pictures of my current product. I would like to manage pictures order with 2 up/down arrows.
When the user clicks on an arrow, it moves up/down the picture compared to the others.
So, on each arrow, there is a link that corresponds to a route action in my ProductController.
It's not very complicated to update only the order of the picture that moved, but I don't know how to update the order of other pictures in the ArrayCollection...
/**
* Manage picture order
*
* #Route("/products/{id}/photos/{idphoto}/move-{direction}", name="prod_move_photo")
*/
public function movePhotoAction($id, $idphoto, $direction, Request $request) {
$em = $this->getDoctrine()->getManager();
$photo = $em->getRepository('AppBundle:ProductPhoto')->find($idphoto);
if ($direction == 'up') {
$order = $photo->getOrder() - 1;
if ($order >= 0)
$photo->setOrder($order);
else
$photo->setOrder(0);
} elseif ($direction == 'down') {
$order = $photo->getOrder() + 1;
$photo->setOrder($order);
} else {
throw $this->createNotFoundException("The type of ordering '" . $direction . "' doesn't exist.");
}
$em->flush();
// redirection
return $this->redirectToRoute('prod_photos', array('id' => $id));
}
Maybe using PHP uksort() ?
It looks like you want to update order fields in two ProductPhotos moved by each other, right? So please try this:
/**
* Manage picture order
*
* #Route("/products/{id}/photos/{idphoto}/move-{direction}", name="prod_move_photo")
*/
public function movePhotoAction($id, $idphoto, $direction, Request $request) {
$em = $this->getDoctrine()->getManager();
$photo = $em->getRepository('AppBundle:ProductPhoto')->find($idphoto);
$order = $photo->getOrder();
switch($direction) {
case 'up':
$newOrder = ($order >= 1) ? ($order - 1) : (int) 0;
break;
case 'down':
$newOrder = $order + 1;
break;
default:
throw $this->createNotFoundException("The type of ordering '" . $direction . "' doesn't exist.");
}
$anotherPhoto = $em->getRepository('AppBundle:ProductPhoto')->findOneByOrder($newOrder);
$photo->setOrder($newOrder);
$anotherPhoto->setOrder($order);
$em->flush();
// redirection
return $this->redirectToRoute('prod_photos', array('id' => $id));
}
The solution of #Snegirekk works very well (and I use it), but here is the solution I found, if it can help...
/**
* Manage picture order
*
* #Route("/products/{id}/photos/{idphoto}/move-{direction}", name="prod_move_photo")
*/
public function movePhotoAction($id, $idphoto, $direction, Request $request) {
$em = $this->getDoctrine()->getManager();
$photo = $em->getRepository('AppBundle:ProductPhoto')->find($idphoto);
// Current order of the photo
$currentPos = $photo->getOrder();
// Determine new order
if ($direction == 'up') {
$newPos = ($currentPos > 0) ? $currentPos - 1 : 0;
} elseif ($direction == 'down') {
$newPos = $currentPos + 1;
} else {
throw $this->createNotFoundException("The type of ordering '" . $direction . "' doesn't exist.");
}
$product = $em->getRepository('AppBundle:Product')->find($id);
// Get product photos with actual order (moveElement() needs an array)
$photos = $product->getPhotos()->toArray();
// Reorder photos in ArrayCollection
$this->moveElement($photos, $currentPos, $newPos);
// Reorder photos in database (with keys of formatted ArrayCollection)
foreach ($photos as $order => $p) {
$p->setOrder($order);
}
$em->flush();
// redirection
return $this->redirectToRoute('prod_photos', array('id' => $id));
}
/**
* Move an array element to a new index
*
* #param array $array Array of elements to sort
* #param integer $currentPos Current position of the element to move
* #param integer $newPos New position of the element to move
* #return void
*/
public function moveElement(&$array, $currentPos, $newPos) {
$out = array_splice($array, $currentPos, 1);
array_splice($array, $newPos, 0, $out);
}

Symfony2 / Typecasting query results to simpeler object

I am using Stof's DoctrineExtension bundle to retrieve my Tree, now I want to convert that tree to an array (which will then in turn get converted to json).
The format of NestedTreeRepository->childrenHierarchy() is not in the correct format though, I want to modify the output so only the node "title" property and the "id" property is returned, and put any children in a "children" subarray. In compliance with this format (JSON):
{
label: 'node1',
children: [
{ label: 'child1' },
{ label: 'child2' }
]
},
{
label: 'node2',
children: [
{ label: 'child3' }
]
}
}
I have tried to following code, this returns the same as childrenHierarchy() but would allow me to modify the query.
$query = $em
->createQueryBuilder()
->select('node')
->from('MyBundle:Page', 'node')
->orderBy('node.root, node.lft', 'ASC')
->getQuery()
;
$nodes = $query->getArrayResult();
[Do magic here]
$tree = $pagerepo->buildTree($nodes);
Is it possible to typecast every node into a much simpler object containing only the following property's:
id
title
a few other ints used for positioning
if I would then run that through json_encode() I would have exactly what I needed.
Any other solutions are of course welcome.
my code for this purpose (just made this a few hours ago)
it's a remake of stof's buildTreeArray function
in the controller (I'm writing this for symfony2):
function gettreeAction {
$query = .... // do your query
$tree = $this->buildTree($query->getArrayResult());
$response = new Response(json_encode($tree));
return $response;
}
private function buildTree($nodes)
{
$nestedTree = array();
$l = 0;
if (count($nodes) > 0) {
// Node Stack. Used to help building the hierarchy
$stack = array();
foreach ($nodes as $child) {
$item = array();
$item['name'] = $child['title'];
$item['id'] = 'page_'.$child['id'];
$item['level'] = $child['level'];
$item['children'] = array();
// Number of stack items
$l = count($stack);
// Check if we're dealing with different levels
while($l > 0 && $stack[$l - 1]['level'] >= $item['level']) {
array_pop($stack);
$l--;
}
// Stack is empty (we are inspecting the root)
if ($l == 0) {
// Assigning the root child
$i = count($nestedTree);
$nestedTree[$i] = $item;
$stack[] = &$nestedTree[$i];
} else {
// Add child to parent
$i = count($stack[$l - 1]['children']);
$stack[$l - 1]['children'][$i] = $item;
$stack[] = &$stack[$l - 1]['children'][$i];
}
}
}
return $nestedTree;
}
works perfectly with jqTree...
I have solved it as following:
public function getPageTreeAction() {
$pagerepo = $this->getDoctrine()->getRepository('MyBundle:Page');
$em = $this->getDoctrine()->getEntityManager();
$query = $em
->createQueryBuilder()
->select('node')
->from('MyCorpBundle:Page', 'node')
->orderBy('node.root, node.lft', 'ASC')
->getQuery();
$flatnodearray = $query->getArrayResult();
$flatsimplenodearray = array();
foreach ($flatnodearray as $currentNode) {
$currentSimpleNode = array();
$currentSimpleNode['id'] = $currentNode['id'];
$currentSimpleNode['lft'] =$currentNode['lft'];
$currentSimpleNode['rgt'] = $currentNode['rgt'];
$currentSimpleNode['lvl'] = $currentNode['lvl'];
$currentSimpleNode['title'] = $currentNode['title'];
$flatsimplenodearray[] = $currentSimpleNode;
}
$tree = $pagerepo->buildTree($flatsimplenodearray);
$response = new Response(json_encode($tree));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
I would use the Stofs Repository function to get the nodes in an hierarchical array:
$repo = $em->getRepository('MyBundle:Page');
$arrayTree = $repo->childrenHierarchy();
And I think there is no other solution than modify that array manually. After you have removed some properties that you dont need, you can json_encode the array and return it.

symfony2 twig render, exception thrown

So in my base template, I have: {% render "EcsCrmBundle:Module:checkClock" %}
Then I created the ModuleController.php...
<?php
namespace Ecs\CrmBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Ecs\CrmBundle\Entity\TimeClock;
class ModuleController extends Controller
{
public function checkClockAction() {
$em = $this->getDoctrine()->getEntityManager();
$user = $this->get('security.context')->getToken()->getUser();
$today = time();
$start = date('Y-m-d 00:00:00');
$entities = $em->getRepository('EcsCrmBundle:TimeClock');
$query = $entities->createQueryBuilder('tc')
->select('tc.in1, tc.out1, tc.in2, tc.out2, tc.in3, tc.out3')
->where('tc.noteBy = :user')
->andWhere('tc.daydate >= :start')
->setParameter('user', $user->getid())
->setParameter('start', $start)
->setMaxResults('1')
->getQuery();
$entities = $query->getSingleResult();
if (empty($entities)) {
$ents = "clocked_out";
$this->get('session')->set('clockedin', 'clocked_out');
} else {
for ($i=1; $i <= 3; $i++) {
if ($entities["in$i"] != NULL) {
$ents = "clocked_in";
if ($i == 1) {
$this->get('session')->set('nextclock', "out$i");
} else {
$x = $i+1;
$this->get('session')->set('nextClock', "out$x");
}
if ($entities["out$i"] != NULL) {
$ents = "clocked_out";
$x = $i+1;
$this->get('session')->set('nextclock', "in$x");
}
if ($entities["out3"] != NULL) {
$ents = "day_done";
}
}
}
}
return $this->render('EcsCrmBundle:Module:topclock.html.twig', array(
'cstat' => $ents,
));
}
}
The problem is, if there is nothing in the database for the specific day for the specific user yet.. i keep getting:
An exception has been thrown during the rendering of a template ("No result was found for query although at least one row was expected.") in ::base.html.twig at line 161.
500 Internal Server Error - Twig_Error_Runtime
1 linked Exception: NoResultException ยป
I know it has something to do with the fact that is no 'result' from the database... but isn't that what i've accomplished by having the if (empty($entities)) { ?? I have no clue to fix it... any help appreciated...
Replace:
$entities = $query->getSingleResult();
With
$entity = $query->getOneOrNullResult();
If you look in Doctrine\ORM\AbstractQuery you will see that getSingleResult expects one and only one results. 0 will through an exception.
I looked at your code a bit more closely and it looks like you actually expect an array of entities. in which case use:
$entities = $query->getResult();

A different service for my Flex app using Zend_Amf

I have an iterator service that works fine already and returns a correctly structured values to my flex application through my Zend Amf server
$contacts = array();
mysql_connect( 'localhost', 'root', 'test' );
mysql_select_db( 'test' );
$res = mysql_query( 'SELECT * FROM contact' );
while( $contact = mysql_fetch_assoc($res) ) {
$contacts []= $contact;
}
return $contacts;
However I would like to adjust this so that I can leverage my MVC structure and achieve the same results.
I have placed an excerpt that can be brought to working condition
$contacts = array();
$table = new Model_DbTable_Contact();
$result = $table->fetchAll();
//Return an array to be consumed by my flex application
foreach ($result as $row)
{
/*do something*/
}
return $contacts;
You'll want to look into ValueObjects. Zend_Amf supports those, and it's a good idea to use that. That way you can have objects that are native to both PHP and Flex.
$server->setClassMap('ContactVO', 'Contact');
Your Flex would then have a class:
[Bindable]
[RemoteClass(alias="Contact")]
public class ContactVO
{
}
Would tell your server that you're going to map your Contact class to ContactVO in Flex.
then you could do:
$data = array();
foreach ($result as $row)
{
$data[] = new Contact($row);
//assuming the Contact constructor parses the array data
}
return $data;
and your Contact objects would get to Flex as ContactVO objects
So here I have a function in the logical model for a database table:
public function fetchAll() {
$resultSet = $this->getDbTable()->fetchAll();
$entries = array();
foreach( $resultSet as $row ) {
$entry = new Model_ClosingData();
$entry->setId($row->id)
->setContractMonth($row->monthId)
->setCommodity($row->commodityId)
->setDate($row->date)
->setPrice($row->price)
->setVolume($row->volume)
->setOutstandingInterest($row->outstandingInterest);
$entries[] = $entry;
}
return $entries;
}

Resources