In a Zend view I can apply a partial template to an iterable element as follows:
$this->partialLoop('template.phtml', $iterable);
However inside the template, only the elements of the $iterable are available, is there another way of passing extra data to the partial?
I use
$this->partialLoop('template.phtml', array(
'data' => $iterable,
'otherVariable' => $otherVariable
);
Warning & Edit:
To be completly honest, I made a mistake. I guess that the code I proposed won't work. I mistaken it for the partial() helper. It won't work because of this part of the helper's class:
foreach ($model as $item) {
// increment the counter variable
$this->partialCounter++;
$content .= $this->partial($name, $module, $item);
}
It will iterate over the whole array instead of the "data" key. I don't get how the answer could be accepted :D Thanks to Nikolaus Dulgeridis for pointing that out.
You can't even post any extra data through $this->view because the point of partial is that it creates "clean" view instance - so that the assigned variables won't collide with your existing variables.
Possible options
- Extend the view helper with methods to set custom variables
- Iterate the array and reformat it to
array(
array('data' => $item1, 'id' => 1, 'totalCount' => 10) ,
array('data' => $item2, 'id' => 2, 'totalCount' => 10) ,
array('data' => $item3, 'id' => 3, 'totalCount' => 10) ,
)
- Use Registry to store the values.
Zend_Registry::set('partialLoopCount', $count);
$this->partialLoop($viewScript, $data);
- Dump partialLoop and use partial() instead
I prefer this solution.
$count = count($data);
foreach ($data as $key => $value) {
echo $this->partial($viewScript, array('item' => $value, 'position' => $key, 'count' => $count));
}
Inside the partial, you can access all of your view variables with:
$this->partialLoop()->view->myVariable
where myVariable is a normal view variable ($this->view->myVariable in the controller or
$this->myVariable in the view, which it's actually the same thing).
Basically, you retrieve the PartialLoop() object, then the view which called it, and then the variable itself.
This, though, will probably impact performance (and I don't think it's really MVC friendly...)
But, hey: it works. :)
An example here:
Hardcode.nl == Joris Osterhaus
Later I found (insert in partial):
$this->getHelper('PartialLoop')->view->otherVariable;
You can access parent view variables by this way :
$this->ViewModel()->getCurrent()->getVariable('parentVariable');
Found at http://blog.dossantos.com.au/how-to-access-parent-view-variables-from-a-partial-loop-in-zf2
In controller
$this->view->otherVariable = 'otherVariable';
In "partial file" - template.phtml
$this->otherVariable
(ZendFramework-1.11.4-minimal)
Related
I have some code that should be deleting a record from an embedded MongoDB document.
Here is the code:
public function actionDeleteSaved()
{
$savedLink = $_POST['savedLink'];
$userId = Yii::app()->user->getId();
$current = SaveLink::model()->findByPk($userId);
if(in_array($savedLink, $current->links))
{
array_slice($current->links, $savedLink);
$current->save();
}
}
This is what is passing the data to the controllers action method:
echo CHtml::ajaxButton(
'delete',
Yii::app()->createUrl("dashboard/index/deletesaved"),
array( // ajax options
'type' => 'POST',
'context' => "js:this",
'data' => array(
'savedLink' => $savedLink
)
),
array( //html options
'class'=>'deleteSaved'
)
);
This is what renderPartial looks like:
$this->renderPartial('_deleteSaved', array('savedLink'=>$s));
What I want being posted is being posted correctly but I'm not sure if it's communicating with the Controller and passing the data through or if my code for removing the data from the database is correct.
Any help would be greatly appreciated, thanks.
The problem is with array_slice part. As specified in php docs array slice does not modify array parameter.
Use array_splice instead (it modifies passed array param) and array_search to get key:
if(in_array($savedLink, $current->links))
{
$key = array_search($savedLink, $current->links);
array_splice($current->links, $key, 0);
$current->save();
}
NOTE: If $current->links is embedded documents (objects) array, you might have to find $key and check if is in array some other way.
I've started to learn Smarty templating engine hoping it would allow me to do what i have a hard time to do with PHP built-in templates, but i'm encountering a similar problem.
Let's assume that i want to create reusable pieces of HTML code like, for example, an accordion menu. My template would look like :
Accordion.tpl :
<div class="Accordion">
{foreach from=$entries item=entry}
<div class="AccordionEntry">
<div class="AccordionTab">
{$entry.tab}
</div>
<div class="AccordionContent">
{$entry.content}
</div>
</div>
{/foreach}
</div>
This template will retrieve the variable "entries" assigned in the controller part :
$smarty = new Smarty();
$smarty->assign('entries', [
['tab' => 'tab_00', 'content' => 'content_00'],
['tab' => 'tab_01', 'content' => 'content_01'],
['tab' => 'tab_02', 'content' => 'content_02']
]);
$smarty->display('Accordion.tpl');
This will work fine.
However, what if i want to reuse this accordion template in multiple places ?
The data could be assigned this way :
$smarty->assign('leftMenuEntries', [
['tab' => 'tab_00', 'content' => 'content_00'],
['tab' => 'tab_01', 'content' => 'content_01'],
['tab' => 'tab_02', 'content' => 'content_02']
]);
$smarty->assign('rightMenuEntries', [
['tab' => 'tab_00', 'content' => 'content_00'],
['tab' => 'tab_01', 'content' => 'content_01'],
['tab' => 'tab_02', 'content' => 'content_02']
]);
Here is my problem : the template "Accordion.tpl" will always retrieve data from the variable "entries", but here i'm using "leftMenuEntries" and "rightMenuEntries", so oviously it will fail. Since the two accordions won't necessarily have the same entries, i'm forced to assign these entries to two different variables.
What can i do to make this work together please ?
Thanks for your help :)
When you {include} one template from another, you can "pass in" variables for use in that template (a bit like function parameters). So if you have an overall template for the page layout, rendered as e.g. $smarty->display('Homepage.tpl'), you can have multiple accordions within it like so:
{* pull entries out of $entries, as nothing else specified *}
{include file=Accordion.tpl}
{* pull entries out of $leftMenuEntries, which will be named as $entries inside the included file *}
{include file=Accordion.tpl entries=$leftMenuEntries}
{* the same, but this time we "pass in" $rightMenuEntries *}
{include file=Accordion.tpl entries=$rightMenuEntries}
Not sure if I understood correctly, but if you want to use the data with the same templating style multiple times, without assigning new variable and displaying to new template, you can possibly create Accordion.tpl with only this div, and include it in every template where you want the data.
{include file='Accordion.tpl'}
In the case above it will retrieve the variable with the same name, if it's assigned to the main template, no to Accordion.tpl
However, if the problem's core is in the overwriting arrays, I found that in array_merge topic in PHP.net:
<?php
// you have two arrays:
array1 = array (['0'] =>"blahblupp",
['1'] => "bluppblah" );
array2 = array (['0'] =>"tirili",
['1'] => "tralala" );
// and want following as a result:
result = array (['0'] =>"blahblupp",
['1'] => "bluppblah",
['2'] =>"tirili",
['3'] => "tralala" );
// following function does the addition:
function array_add($array1, $array2)
{
$result = $array1;
$h = sizeof($array1);
for ($i = 0; $i < sizeof($array2); $i++)
{
$result[$h] = $array2[$i];
$h++;
}
return $result;
}
?>
If it's still not your case, I will try to find another solution
I've got this input form: (Using the blade template engine with Laravel, but the html should be easy to understand from this and ultimately trivial)
{{ Form::text('amount[]', Input::old('amount')) }}
<?php echo Form::select('unit[]',
array(
'whole' => _('whole'),
'ml' => _('milliliter'),
'l' => _('liter'),
'dl' => _('deciliter'),
'mg' => _('milligram'),
'g' => _('gram'),
'kg' => _('kilogram'),
'tsp' => _('teaspoon'),
'tbs' => _('tablespoon'),
)) ?>
{{ Form::text('ingredient[]', Input::old('ingredient')) }}
I'm trying to format this to my database to return it in a string like this :
<li><span>1</span> liter red wine</li>
I'm considering making it a simpler form and eliminating the unit measurement forcing my users to type it in instead for flexibility, but I'll still have to cramp it all into one table for my database. The span tag is used in a jQuery to dynamically increase the number so is needed. I've been at this for quite a few days on and off but I can't crack how to do this.
Here is my formatting logic:
$amount = Input::get('amount[]');
$unit = Input::get('unit[]');
$ingredient = Input::get('ingredient[]');
for ( $i = 0, $c = count(Input::get('ingredient[]')); $i < $c; $i++ )
{
$ingredients .= '<li><span>'.$amount[$i].'</span>'.$unit[$i].' '.$ingredient[$i].'</li>';
}
and I send it using
$new = Recipe::create(array(
'title' => Input::get('title'),
'curiousity' => Input::get('curiousity'),
'ingredients' => $ingredients,
'steps' => Input::get('recipe')
));
I've tried numerous ways and I get errors like the $ingredients array not being defined or not being able to use [] in the variable. I tried defining the variable as an '$ingredients = '';' variable but that just produced an empty string. My problem must be in the logic.
Build your select list outside the form for brevity as well as (what I do anyway to keep controllers very slim) send the input to the model all at once.
$input = Input::all();
$new = Recipe::create($input);
Build the array for the ingredients elsewhere. In the model (perhaps?):
$ingredients = array(
'dbname1' => 'displayname1',
'dbname2' => 'displayname2'
);
And display it accordingly, then the form inputs should be sent over with the $input in an array that you can parse and then save to your db.
Other notes about Blade syntax. I'm not aware of a need to define the array brackets [].
{{Form::open()}}
{{Form::label('label1','Display Label 1')}}
{{Form::text('fieldname1',Input::old('fieldname1'))}}
With your ingredients array already built (your current syntax will produce a dropdown and I assume you want checkboxes)
{{Form::select('ingredientsFIELDNAME',$ingredients)}}
{{Form::close()}}
In your Input::all() array your ingredientsFIELDNAME field name will have an array if you've built it as checkbox instead of select. Hope this all makes sense.
I'd like to exclude results from a call to a Lithium model's find() method. I need to do this for models with both MongoDB and MySQL data sources, but in SQL I mean something like WHERE myfield NOT IN (1,2,3).
I'd like to just be able to pass a not clause in the conditions array like below, but that doesn't appear to be possible.
Item::all(array('conditions' => array('not' => array('myfield' => array(1,2,3))));
So my question is, is this possible in Lithium in a way that I've overlooked? And if not, what would be the most Lithium-ish way to implement it for my models?
Just to clarify, Lithium's MongoDB adapter supports most SQL comparison operators as a convenience, so for either Mongo or MySQL, you could simply write the query as follows:
Item::all(array('conditions' => array(
'myfield' => array('!=' => array(1,2,3))
)));
And it should give you the results you expect. For MySQL, the query should look something like:
SELECT * FROM items WHERE myfield NOT IN (1, 2, 3);
And in Mongo:
db.items.find({ myfield: { $nin: [1, 2, 3] }})
Merely filtering for MongoDB can easily be achieved like this:
Item::all(array('conditions' =>
array('myfield' => array(
'$nin' => array(1,2,3)
))
));
If this is something you do a lot you could even create a custom finder for it :
class MyModel extends \lithium\data\Model {
public static function __init()
{
parent::__init();
static::finder('notin', function($self, $params, $chain) {
// Take all array keys that are not option keys
$array = array_diff_key($params['options'],
array_fill_keys(array('conditions', 'fields','order','limit','page'),0));
// Clean up options leaving only what li3 expects
$params['options'] = array_diff_key($params['options'], $array);
$params['options']['conditions'] = array(
'myfield' => array(
'$nin' => $array
)
);
return $chain->next($self, $params, $chain);
});
}
}
And call it like this :
MyModel::notin(array(1,2,3));
In the same manner you could create a custom finder for MySQL sources.
As you probably can see this creates some issues if you pass something like array('fields'=>$array) as it would overwrite the option.
What happens is that ::notin() (finders in general) has a distinct behavior for the (array,null) signature. If that happens it thinks the first array is options and the finder took no arguments.
Using notin($array,array()) breaks the previous finder because the first argument ends up in $params['notin'] when the real second argument (options) is passed.
If you mix data sources on the fly here I would create a custom model that does not inherit \lithium\data\Model and have it delegate
to the different models and create the conditions based on the end models data source.
class MyFacadeModel {
public static function byNotIn($conditions, $source) {
return ($source == "mongodb")
? $source::find( $rewrittenConditions)
: $source::find( $rewrittenConditionsForMysql );
}
}
(Code might be slightly incorrect as its mostly taken from the top of my head)
I happened to be making some changes to a WordPress blog and noticed that they use parse_str (http://php.net/parse_str) for parsing and setting their optional parameters to a function.
I'm wondering if there is an advantage to this over sending an array?
Examples:
With array:
$blahOptions = array(
'option_1' => true,
);
BlahArray($blahOptions);
function BlahArray($options = array()) {
$defaults = array(
'option_1' => false,
'option_2' => 'blah',
);
// this would probably be in a function to be used everywhere
foreach ($defaults as $defaultOption => $defaultValue) {
if (!isset($options[$defaultOption])) $options[$defaultOption] = $defaultValue;
}
}
With parse_str:
$blahOptions = 'option_1=1';
BlahString($blahOptions);
function BlahString($options = '') {
$defaults = array(
'option_1' => false,
'option_2' => 'blah',
);
parse_str($options, $defaults);
$options = $defaults;
}
No. That seems like a ridiculous way to pass functional parameter arguments. I could understand it if you needed to recreate $_GET or $_POST variables or something along those lines, but for parameters to a function? That's code smell right there.
They should be using an array, and then utilizing extract() instead. I've worked with Wordpress before, and my advice is to keep the code at arm's length. It is not an example of model programming.
No. There are more disadvantages than advantages.
When you’re using a single string, you just can pass string values. With an array you can use every PHP data type and every element’s value type is independently of each other.
With parse_str, you can potentially drop in the query string from the URL, and the function will work. If you use an array, and you want to use the query string, you'll have to enumerate everything into an array before calling the function.
I'm not totally convinced it's the best way to go, but I see how it can add a bit of flexibility.