Real dynamic DQL with doctrine in symfony 3.4 - symfony

I'm trying to achieve a dynamic query with DQL in doctrine. I've checked several post about this subject but all the solutions are static. I want to achieve somethin like this:
$qb->where(
$qb->expr()->orX(
$qb->expr()->like('e.cliente', ':cliente_tag'),
$qb->expr()->like('e.cliente', ':cliente_tag2'),
$qb->expr()->like('e.cliente', ':cliente_tag3')
),
$qb->expr()->orX(
$qb->expr()->like('e.apoderado', ':apoderado_tag'),
$qb->expr()->like('e.apoderado', ':apoderado_tag2'),
$qb->expr()->like('e.apoderado', ':apoderado_tag3')
)
);
but inside a loop like this:
foreach ($options['camposTexto'] as $i => $campoTexto) {
switch ($campoTexto['appliedTo']) {
case 'apoderado': {
$exp = [];
foreach ($campoTexto['tags'] as $tag) {
$exp[] = $qb->expr()->like('e.apoderado', ':apoderado_tag' . $i);
$parameters['apoderado_tag' . $i] = '%' . $tag . '%';
}
if ($isFirst) {
$isFirst = false;
$qb->where($qb->expr()->orX($exp));
} else {
$qb->andWhere($qb->expr()->orX($exp));
}
break;
}
case 'cliente': {
$exp = [];
foreach ($campoTexto['tags'] as $tag) {
$expresiones[] = $qb->expr()->like('e.cliente', ':cliente_tag' . $i);
$parameters['cliente_tag' . $i] = '%' . $tag . '%';
}
if ($isFirst) {
$isFirst = false;
$qb->where($qb->expr()->orX($exp));
} else {
$qb->andWhere($qb->expr()->orX($exp));
}
break;
}
}
}
tags is an array of strings. As you see I passed the array of expresions but doctrine throws me an Exception.
So far so now I have not found any solution to my problem.
Any Idea?
Thanks in advance!

Looking at this post I figured out the solution. It would be something like this:
case 'apoderado': {
$orX = $qb->expr()->orX();
foreach ($campoTexto['tags'] as $y => $tag) {
$orX->add($qb->expr()->like('e.apoderado', $qb->expr()->literal('%' . $tag . '%'))); //<= with literal because I can't set the parameters later in the qb
}
$expresiones[] = $orX;
break;
}
after all the case/break
$andX = $qb->expr()->andX();
$qb->where($andX->addMultiple($expresiones));
return $qb->getQuery()->getResult();

Related

How to override the Drupal plugin's function?

I am new in Drupal.
I am using imagemagick plugin for modify images. My problem is when it resize GIF image the image got messed up. I search the solution on internet i found the changes regarding that.
but i don't want to write code changes directly to plugins module file.
I want to override it's image_imagemagick_resize() function. I can not figure out how to Override it and where to put it's override function.
Default Function :-
function image_imagemagick_resize(stdClass $image, $width, $height) {
$image->ops[] = '-resize ' . (int) $width . 'x' . (int) $height . '!';
$image->info['width'] = $width;
$image->info['height'] = $height;
return TRUE;
}
Override Function:
function imageapi_imagemagick_image_resize(&$image, $width, $height) {
$extra = '';
if($image->info['mime_type'] == 'image/gif') {
$extra = '-coalesce ';
}
$image->ops[] = $extra . '-resize '. (int) $width .'x'. (int) $height .'!';
$image->info['width'] = $width;
$image->info['height'] = $height;
return TRUE;
}
Thanks
Hopefully image_imagemagick_resize invokes alter hooks via _image_imagemagick_alter_invoke :
function image_imagemagick_resize($source, $dest, $width, $height) {
$args = array('resize' => '-resize ' . (int) $width . 'x' . (int) $height . '!');
$args = _image_imagemagick_alter_invoke('resize', $source, $args);
return _image_imagemagick_convert($source, $dest, $args);
}
function _image_imagemagick_alter_invoke($op, $filepath, $args) {
foreach (module_implements('imagemagick_alter') as $module) {
$function = $module . '_imagemagick_alter';
$function($op, $filepath, $args);
}
return $args;
}
So you can override the resize parameters by implementing the hook_imagemagick_alter() in a custom module :
function mymodule_imagemagick_alter($op, $filepath, &$args) {
# altering $args...
}

InvalidArgumentException - The current node list is empty.

I am using goutte sracper to scrape the data , i m getting error like InvalidArgumentException - The current node list is empty. Below is the code which i m using
$string = $crawler->filter('div#links.results')->html();
if ( empty( $string ) )
return false;
$dom = new \DOMDocument;
$state = libxml_use_internal_errors(true);
$dom->loadHTML($string);
libxml_use_internal_errors($state);
$xp = new \DOMXPath($dom);
$divNodeList = $xp->query('//div[contains(#class, "results_links_deep")]
[contains(#class, "web-result")]
/div[contains(#class, "links_main")]
[contains(#class, "links_deep")]
[contains(#class, "result__body")]');
$results = [];
if(count($divNodeList) > 0){
foreach ($divNodeList as $divNode) {
$results[] = [
trim($xp->evaluate('string(./h2/a[#class="result__a"])', $divNode)),
trim($xp->evaluate('string(.//a[#class="result__snippet"])', $divNode)),
trim($xp->evaluate('string(.//a[#class="result__url"])', $divNode))
];
}
}
I tried using the solution as below
if ($crawler->filter('div#links.results')->count() > 0 ) {
$string = $crawler->filter('div#links.results')->html()
}
then it started giving another error like DOMDocument::loadHTML(): Empty string supplied as input
Any suggestions please ?
Your filterdid not return any results. That is why it crashed. That's how I solved this issue, by adding a try catch.
try {
$string = $crawler->filter('div#links.results')->html()
} catch (\InvalidArgumentException $e) {
// Handle the current node list is empty..
}

How to export all rows as CSV in ModelAdmin (SilverStripe 3.1)?

Apparently the GridFieldExportButton only exports the currently visible data-set (paginated). Is there a way to make it export all the rows from a model?
Or alternatively: Is there a way to show all rows (eg. bypass pagination), so that the user can perform an export after showing all the rows? I don't want to show all rows all the time (which would probably be possible by setting ModelAdmin::set_page_length(<ridiculouslyHighNumber>);) but only on demand.
You can override ModelAdmin::getExportFields() to define the columns you want to export.
The method needs to return an array with column name as the key, and the db field as the value.
For example:
class MyCustomModelAdmin extends ModelAdmin {
....
public function getExportFields() {
return array(
'FirstName' => 'FirstName',
'Surname' => 'Surname',
'Age' => 'Age'
);
}
}
Solved it by creating a custom subclass of the GridFieldExportButton and using this for my models. The key is to use $gridField->getList(); instead of $gridField->getManipulatedList(); in the generateExportFileData method.
Here's the complete class for anybody interested:
class GridFieldExportAllButton extends GridFieldExportButton {
/**
* Generate export fields for CSV.
*
* #param GridField $gridField
* #return array
*/
public function generateExportFileData($gridField) {
$separator = $this->csvSeparator;
$csvColumns = ($this->exportColumns)
? $this->exportColumns
: singleton($gridField->getModelClass())->summaryFields();
$fileData = '';
$columnData = array();
$fieldItems = new ArrayList();
if($this->csvHasHeader) {
$headers = array();
// determine the CSV headers. If a field is callable (e.g. anonymous function) then use the
// source name as the header instead
foreach($csvColumns as $columnSource => $columnHeader) {
$headers[] = (!is_string($columnHeader) && is_callable($columnHeader)) ? $columnSource : $columnHeader;
}
$fileData .= "\"" . implode("\"{$separator}\"", array_values($headers)) . "\"";
$fileData .= "\n";
}
$items = $gridField->getList();
foreach($items as $item) {
$columnData = array();
foreach($csvColumns as $columnSource => $columnHeader) {
if(!is_string($columnHeader) && is_callable($columnHeader)) {
if($item->hasMethod($columnSource)) {
$relObj = $item->{$columnSource}();
} else {
$relObj = $item->relObject($columnSource);
}
$value = $columnHeader($relObj);
} else {
$value = $gridField->getDataFieldValue($item, $columnSource);
}
$value = str_replace(array("\r", "\n"), "\n", $value);
$columnData[] = '"' . str_replace('"', '\"', $value) . '"';
}
$fileData .= implode($separator, $columnData);
$fileData .= "\n";
$item->destroy();
}
return $fileData;
}
}
Thanks for this!
I had to use this for Members GF in Security Admin.
Created an extension for anyone interested.
class SecurityAdminExtension extends Extension{
function updateEditForm($form){
$gf = $form->Fields()->fieldByName('Root.Users.Members');
$gfConfig = $gf->getConfig();
$gfConfig->removeComponentsByType('GridFieldExportButton');
$gfConfig->addComponent(new GridFieldExportAllButton());
}
}
I while back, I created a little plugin to make it easy to export DataObjects to CSV or Excel files.
https://github.com/firebrandhq/excel-export
It comes with a button you can add to a grid field.
It's got a dependency on PHP-Excel.

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.

Restrict menu tree to first level

I've been struggling a while in order to get my primary links to display only the first level entries (roots). I have the following code in my template.php :
I've tried changing the $level variable to 0 but no effect. I don't know where (the hell) to stop the recursion.
function supertheme_navigation_links($menu_name, $level = 2) {
// Don't even bother querying the menu table if no menu is specified.
if (empty($menu_name)) {
return array();
}
// Get the menu hierarchy for the current page.
$tree_page = menu_tree_page_data($menu_name);
// Also get the full menu hierarchy.
$tree_all = menu_tree_all_data($menu_name);
// Go down the active trail until the right level is reached.
while ($level-- > 0 && $tree_page) {
// Loop through the current level's items until we find one that is in trail.
while ($item = array_shift($tree_page)) {
if ($item['link']['in_active_trail']) {
// If the item is in the active trail, we continue in the subtree.
$tree_page = empty($item['below']) ? array() : $item['below'];
break;
}
}
}
return supertheme_navigation_links_level($tree_page, $tree_all);
}
/**
* Helper function for supertheme_navigation_links to recursively create an array of links.
* (Both trees are required in order to include every menu item and active trail info.)
*/
function supertheme_navigation_links_level($tree_page, $tree_all) {
$links = array();
foreach ($tree_all as $key => $item) {
$item_page = $tree_page[$key];
$item_all = $tree_all[$key];
if (!$item_all['link']['hidden']) {
$l = $item_all['link']['localized_options'];
$l['href'] = $item_all['link']['href'];
$l['title'] = $item_all['link']['title'];
if ($item_page['link']['in_active_trail']) {
if (empty($l['attributes']['class'])) {
$l['attributes']['class'] = 'active-trail';
}
else {
$l['attributes']['class'] .= ' active-trail';
}
}
if ($item_all['below']) {
$l['children'] = supertheme_navigation_links_level($item_page['below'], $item_all['below']);
}
// Keyed with unique menu id to generate classes from theme_links().
$links['menu-'. $item_all['link']['mlid']] = $l;
}
}
return $links;
}
/**
* Return a themed set of links. (Extended to support multidimensional arrays of links.)
*/
function supertheme_links($links, $attributes = array('class' => 'links')) {
$output = '';
if (count($links) > 0) {
$output = '<ul'. drupal_attributes($attributes) .'>';
$num_links = count($links);
$i = 1;
foreach ($links as $key => $link) {
$class = $key;
// Add first, last and active classes to the list of links to help out themers.
if ($i == 1) {
$class .= ' first';
}
if ($i == $num_links) {
$class .= ' last';
}
if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page()))) {
$class .= ' active';
}
// Added: if the link has child items, add a haschildren class
if (isset($link['children'])) {
$class .= ' haschildren';
}
$output .= '<li'. drupal_attributes(array('class' => $class)) .'>';
if (isset($link['href'])) {
// Pass in $link as $options, they share the same keys.
$output .= l($link['title'], $link['href'], $link);
}
else if (!empty($link['title'])) {
// Some links are actually not links, but we wrap these in <span> for adding title and class attributes
if (empty($link['html'])) {
$link['title'] = check_plain($link['title']);
}
$span_attributes = '';
if (isset($link['attributes'])) {
$span_attributes = drupal_attributes($link['attributes']);
}
$output .= '<span'. $span_attributes .'>'. $link['title'] .'</span>';
}
// Added: if the link has child items, print them out recursively
if (isset($link['children'])) {
$output .= "\n" . theme('links', $link['children'], array());
}
$i++;
$output .= "</li>\n";
}
$output .= '</ul>';
}
return $output;
}
function supertheme_primary_links() {
return supertheme_navigation_links(variable_get('menu_primary_links_source', 'primary-links'
));
}
3 comments:
Not totally sure what you meant when you said you tried to change $level to 0, but then it will not enter in the loop: while ($level-- > 0 && $tree_page)
To be able to stop a recursion, the recursive function needs to have an argument with the depth. So INSIDE the recursive function you decide when to stop: if ($depth <1) { return; } and everytime you call the recursive funcion you call it with $depth-1
If you want this functionality 'out-of-the-box', try http://drupal.org/project/menu_block , or get some inspiration from its code: (see menu_tree_depth_trim function)
I believe, that if you set your menu to be non-expanded under menu settings that Drupal will handle the rest for you.
Another option would be to use nice menus which mainly is used to show expandable menus, but also can be used to control the level of menu expansion.

Resources