Dumping a drupal 7 render array to file - arrays

I'm having a hard time outputting some array data to an XML file. Here's the workflow:
Get all relevant data (in this case, a collection of videos and the necessary taxonomy) from the DB.
Loop each returned object, cleaning it up a bit (field combinations, etc).
Loop each returned object, returning an XML node by use of a template file (templates/module_name_xml_entity.tpl.php).
Put all the XML nodes into a wrapper XML template (templates/module_name_xml_wrapper.tpl.php).
Save the wrapper (now including the repeated nodes) into a file on the filesystem.
I have been able to complete this workflow if I manually write XML inline (eg: $xml .= ' ' . $data['field'] . '';
That's not optimal however, and I've been asked to use render arrays instead (and to keep my template files within the module).
So, #'s 1, 2, 5 I can figure out (since saving a file is the same). It's #3 that is the real bugger.
My code:
The dump from the db query results in an array of video objects (title, thumbnail, tags, etc). I convert that to the following:
Array (
[#template] => module_name_xml_entry,
[#video] => stdClass Object (
[title], [thumbnail]....
),
[#theme] => module_name_xml_entry,
)...
Now here's something interesting: if I dd() the array (there's 990 of them), I see that "#children" and "#printed" has been added automagically, therefore I assume I'm working with a real render array.
I then try every darn way I can think of to convert this array into XML. I've tried $xmlOut .= render($theStuffAbove), drupal_render($youguessedit), please_lord_make_it_go($facepalm)... no avail.
What I get out is either blank (nothing is in $xmlOut) or the array itself.
Again, I can loop my DB results, convert the result into XML manually (string building mess) and save all that out just fine. It's the using of render arrays that baffles me. Reading "TDGD7" hasn't helped (there's only a few short pages out of 1047 on render arrays), and I'm just not understanding how render arrays can be "rendered."
Update:
I forgot to mention I do have a module_name_theme(...) function setup:
$items = array();
$items['module_name_admin_settings_form'] = array(...);
$items['module_name_xml_wrapper'] = array(
'variables' => array('videos' => NULL)),
'template' => 'templates/module_name_xml_wrapper',
);
$items['module_name_xml_entity'] = array(
'variables' => array('video' => array()),
'template' => 'templates/module_name_xml_entity',
);
return $items;

Fixed
Ok, so this is odd (swear I'd done this before).
Changed template filenames to use dashes instead of underscores (eg: 'templates/module_name_xml_entry.tpl.php -> module-name-xml-entry.tpl.php)
Changed reference in the hook_theme() to use the dashed names instead
Inside my functions, I used a $variables = array('video' => $video);
I called $output[] = theme('module_name_xml_entry', $variables);
I used $output in my wrapper (module_name_xml_wrapper) to save to the filesystem.
So the long and short: it looks like cleaning up the theme() function and my hook_theme() made the output work finally.

Related

CakePHP3: How to get the query result count if the result is a collection?

I have the following code:
$sites = $this->Sites->find()
->contain([
'Sitecategories',
'Sitedescriptions.Languages',
'Countries',
'Users'
])
->where([
'postcode LIKE' => $this->request->data['numero'] . '%'
])
->sortBy(function($row) { return substr($row->postcode, 0, 2); }, SORT_ASC);
debug($sites); displays:
object(Cake\Collection\Iterator\SortIterator) {
'count' => (int) 428
}
But I don't understand how to access to that count var.
I tried to access $sites->count() but I get the following error message:
Error: You cannot issue a count on a Collection.
Counting collections is kinda unreliable, given than they can mutate when being iterated, ie the count and the content can change, and with every iteration, changes may be reapplied for certain operations, causing the collection to possibly mutate in an unwated way (filtering, reducing, modifying contained objects, etc). Another unwanted effect may be unrewindable collections, ie they could not be iterated anymore. So that's basically why no count method is implemented.
The debug info shown there is the return value of iterator_count(), it accepts traversable objects and returns the number of elements in the iterator. Calling this will have the aforementioned side effects, the collection will be iterated and possibly mutated.
If you really need to know the count, and want to keep the collection, then you could for example compile it in beforehand, that would create a new collection based on the source data with all modifications applied, like:
$sites = $sites->compile();
$count = iterator_count($sites);
$sites can safely be reused after that point.
If you don't need the collection anymore, you could always simply convert it into an array and count that:
$sites = $sites->toArray();
$count = count($sites);
See also
PHP Manual > iterator_count()
Cookbook > Collections > Collection::compile()
Another possible solution is to use buffered iterator.
$collection = new Collection([1,2,3]);
$collection->count(); //throws exception
$collection->buffered()->count(); //gives 3
You have to use count($sites->toArray()).

How to strip HTML from a field in drupal views

I am trying to add a function to that strips the html from a field in drupal views. I found a function for sql server called "udf_StripHTML" that does that.
http://blog.sqlauthority.com/2007/06/16/sql-server-udf-user-defined-function-to-strip-html-parse-html-no-regular-expression/
I am using the following code:
/**
* Implements hook_views_query_alter().
*/
function cviews_views_query_alter(&$view, &$query) {
// Add a strip html tags from content.
$fields = array('field_data_body.body_value');
foreach ($query->where as $key1 => $value) {
foreach ($value['conditions'] as $key2 => $coditions) {
if (in_array($coditions['field'], $fields)) {
$query->where[$key1]['conditions'][$key2]['field'] = 'dbo.udf_StripHTML(' . $coditions['field'] . ')';
}
}
}
}
When views module converts the query object to a string the field become
from:
'dbo.udf_StripHTML(field_data_body.body_value)';
to:
[dbo].[udf_StripHTMLfield_data_body.body_value]
My question is, how can I add a function there?
Thank you,
You are going way too deep here friend. I'm going to assume that you're using Drupal 7, but for Drupal 8 this should be similar (since views is in core for both).
A few things about your approach:
That function is a user defined function which means that it needs to be defined at a much lower-level (in the SQL database) before you can use it in your query.
This is a red-herring approach, however, because you don't need to even touch the SQL to accomplish what you want (you can do this with PHP with strip_tags!)
You don't need a query alter hook here (we don't need to go to the database to do this). You could do this with one of the preprocess or field hooks from the field API or the views API using the function linked in my previous point.
Even better, you don't even have to touch the code to accomplish this. You can do it right in the Drupal UI.
Under the field settings for the view, select rewrite results and then Strip HTML tags. Presto, no more HTML tags in that field.
Image source: https://www.drupal.org/node/750172
Here is the solution that worked for me:
// Traverse through the 'where' part of the query.
foreach ($query->where as &$condition_group) {
foreach ($condition_group['conditions'] as &$condition) {
if (in_array($condition['field'], $fields)) {
$value = $condition['value'];
$field = $condition['field'];
$condition = array(
'value' => array(),
'field' => t('dbo.udf_StripHTML(!field) like \'#value\'', array(
'!field' => $field,
'#value' => $value)),
'operator' => 'formula',);
}
}
}

CodeIgniter - List of files and folders using opendir

I'm fairly new to certain programming techniques. Very new to OOP and MVC in general. And I'm pretty sure this is my first StackOverflow question!
I've just downloaded CodeIgniter and have a little project for myself.
I have a list of files and folders on the server and would like to use opendir, readdir and closedir etc to list out them out on a web page in ul's and li's - I've done this in procedural before in a function but have no idea where to even begin with CodeIgniter.
Is there a Helper or Library that already does this? If not, what is the best method? Should I put my code in the model folder?
So confused!
I hope you have learned about MVC architecture already in past year :)
Now about your question. This helper or library you have asked for really exists. For the very same "problem" I have used directory helper and its function directory_map('relative/path/to/your/directory'). This function gets recursively content from your directory and sorts it into array like this
Array
(
[banner] => Array
(
[0] => banner1.jpg
[1] => banner2.jpg
[2] => banner3.jpg
[3] => banner4.jpg
)
[galerie] => Array
(
[0] => 0-PB083393.JPG
[1] => DSCN2897.JPG
[2] => DSCN2908.JPG
[3] => DSCN2917.JPG
[thumb] => Array
(
[0] => 0-PB083393_thumb.JPG
[1] => DSCN2897_thumb.JPG
[2] => DSCN2908_thumb.JPG
)
)
[0] => mapa.jpg
)
which is quite neat and you can use it in - for example - foreach cycle and add ul/li tags.
Probably this question is not relevant after one year, but I hope it could help others.
Ha. This is funny. I was looking for something else and stumbled on to my first ever CI question without realising it was me :D
I've come so far with CI in just less than a month.
I found Directory Helper - directory_map that basically puts your folder structure in to an array of arrays.
I them created a recursive function in the model that turns it in to a proper drop down menu... And when it's a file, it adds in an a href link to that file.
http://ellislab.com/codeigniter/user-guide/helpers/directory_helper.html
If I were doing this, I would:
(1) Create a Library class with a method that takes a directory name and returns an
array of files.
(2) In my controller, I would then load the library and use it to get the list of files for the folder of interest.
(3) I would then load a view while passing the array of file names to the view where
I would assemble the list.
Start by learning how to use the controller to load a view with data (start with a static array). Then learn how to create the library and integrate with your controller.
You might also read up about CodeIgniter's File Helper library unless you want to use the native PHP functions.
Also, learn about PHP unit testing.
I tend to use models for dealing with data from MySQL databases. In your case, you are dealing with information about your file system.
Good luck, CI is a good choice for a PHP/MySQL framework!
First, welcome to CodeIgniter. It rules. Now...
You need a controller function to actually process the directory, similar to this:
public function dir_to_array($dir, $separator = DIRECTORY_SEPARATOR, $paths = 'relative')
{
$result = array();
$cdir = scandir($dir);
foreach ($cdir as $key => $value)
{
if (!in_array($value, array(".", "..")))
{
if (is_dir($dir . $separator . $value))
{
$result[$value] = $this->dir_to_array($dir . $separator . $value, $separator, $paths);
}
else
{
if ($paths == 'relative')
{
$result[] = $dir . '/' . $value;
}
elseif ($paths == 'absolute')
{
$result[] = base_url() . $dir . '/' . $value;
}
}
}
}
return $result;
}
Now you need to call that function to return the results, similar to:
$modules['module_files'] = $this->dir_to_array(APPPATH . 'modules');
This will put the results in a variable called $modules, which you can use in whichever way you want, typically put it in a view like this:
$this->load->view('folder/file', $modules);
If you provide an optional third parameter of TRUE to the load->view function, the result of that view will again be returned for you to use anywhere you like, otherwise it will be echoed out where you call it. The view may look something like this:
<?php
if (isset($module_files) && !empty($module_files))
{
$out = '<ul>';
foreach ($module_files as $module_file)
{
if (!is_array($module_file))
{
// the item is not an array, so add it to the list.
$out .= '<li>' . $module_file . '</li>';
}
else
{
// Looping code here, as you're dealing with a multi-level array.
// Either do recursion (see controller function for example) or add another
// foreach here if you know exactly how deep your nested list will be.
}
}
$out .= '</ul>';
echo $out;
}
?>
I have not checked this for syntax errors, but it should work fine. Hope this helps..

unable to retrieve image from custom content type

I've just started learning how to use Drupal 7. I made a new content type that will be displayed as a feed in my front page. All the data I need are being fetched and displayed correctly except for the image, whose url is always missing the actual file. I have another feed in my front page that uses the default article content type and all the images display properly.
The code I used for both is essentially the same, the only difference being the content type being retrieved.
This set worked:
$query = db_select('node', 'n');
$query->fields('n', array('nid', 'title'))
->condition('n.type', 'article')
->leftJoin('field_data_body', 'u', 'u.entity_id = n.nid');
$query->addField('u', 'body_summary');
$query->orderBy("nid", "desc");
$query->range(0, 3);
$result = $query->execute();
while($row = $result->fetchAssoc()) {
$nid = $row['nid'];
$node = node_load($nid);
echo theme('image_style', array('style_name' => 'home-article-summary', 'path' => ($node->field_image['und'][0]['uri'])));
}
This didn't:
$query = db_select('node', 'n');
$query->fields('n', array('nid', 'title'))
->condition('n.type', 'news') //the only difference between the two is this line
->leftJoin('field_data_body', 'u', 'u.entity_id = n.nid');
$query->addField('u', 'body_summary');
$query->orderBy("nid", "desc");
$query->range(0, 3);
$result = $query->execute();
while($row = $result->fetchAssoc()) {
$nid = $row['nid'];
$node = node_load($nid);
echo theme('image_style', array('style_name' => 'home-article-summary', 'path' => ($node->field_image['und'][0]['uri'])));
}
I tried making the settings of the content type I created the same as the ones for article throught Structure->Content Types but nothing happened. What am I missing? Thank you.
edit:
Upon inspection, I couldn't find a resized version of the image I uploaded for my custom content type. I'm assuming this means no resizing ever actually happened hence why the script didn't return a file. I still don't get why that happened.
edit the second:
never mind. I found the problem. it's all working fine now. was just using the wrong variable.
As a bit of friendly advice for a Drupal newcomer, consider using Views to display lists of content instead of writing heaps of custom database queries. That will save you lots of coding in the long run.
Another argument for Views is that it's going to be included in Drupal 8 core.

CakePHP append to Containable/Contain

How to append to contain when it's already declared?
I call a regular contain/find:
// A controller
$this->Site->contain(array('User'));
$site = $this->Site->find();
I want to automatically add something to contain. I was thinking of doing this in the Model by adding to the find function... something like this:
// Site.php Model
function find($conditions = null, $fields = array(), $order = null, $recursive = null) {
if(!isset($this->containVariable) || !in_array('Box', $this->containVariable)) {
$this->containVariable[] = 'Box';
}
parent::find($conditions, $fields, $order, $recursive);
}
To make this work (automatically adding model Box to contain) I only need to change $this->containVariable by a function or variable that has an array of what's already in contain. In this case this would return array('User'). How to append to contain when it's already declared? Is there a variable that contains contain?
Unless someone can find another solution I've came to the conclusion that:
Unfortunately what I was trying to do seems impossible how containable is designed. I had to manually add my model to every ->contain() calls.
I fixed perhaps a similar issue with persisting contain for multiple find calls (using cakephp 1.3). After declaring contain for a certain model, this will be set for that model:
$this->Behaviors->Containable->runtime
-where $this is some model object. 'runtime' is an array that essentially holds the contain information. So you are close I believe, but instead of:
$this->containVariable[] = 'Box'
You would have:
$this->Behaviors->Containable->runtime['someModel']['contain'][] = 'Box'
To be able to specify more than just the model though, you would have to set up to handle like the following (perhaps build a method accordingly):
$this->Behaviors->Containable->runtime['someModel']['contain'][] = $model_contain_array
$model_contain_array is just an arbitrary name I just chose for an array which holds the contain information you would like to add for a particular model. An example of $model_contain_array might be:
array('modelName' => array(
'fields' => array('id', 'other'),
'otherModelName' => array(
'fields' => array('id', 'otherfield', 'etc')
)
)

Resources