Clean way of passing variable to partial view - php

I'm looking for a clean way to pass variables to partial views. Consider the following example code:
In my controller I do:
$this->view->articles = $arrayWithArticles;
$this->render('articles.phtml');
In my articles.phtml view I do:
foreach($this->articles as $article) {
// show article
$this->render('comments.phtml');
}
In another controller I do:
$this->view->products = $arrayWithProducts;
$this->render('products.phtml');
In my products.phtml view I do:
foreach($this->products as $product) {
// show product
$this->render('comments.phtml');
}
As you can see I use the same (partial) view comments.phtml to display comments about written articles as well as products. The 'comments' I want to display are in $article->comments and $product->reviews. The partial view will need these to display them.
What would be a clean way to pass them to the partial view. I really don't want to do:
$this->comments = $article->comments;
$this->render('comments.phtml');
Because this would potentially become a pain to keep track off (i.e. setting the same view variables in both the controller as in the view).
Is there a clean solution to pass variables to partial views?

Well, I think adding a parameter to your render() method would be sufficient. Maybe something like...
$this->renderSubView($fileName, $data);
Then in renderSubView() you could do whatever it is that you need to do with the array and return the rendered partial view. This way you don't need to redeclare the variable in view, just pass the data appropriate for that specific partial when it is being rendered.

Related

Silverstripe customize controller data/variables

I'm trying to return search results to a new controller than where the search action was performed from. Problem is Results is never accessible from CustomSearchResultPage.ss. I've added inline comments for what I think is happening, am I right in my thinking here?
// Customise content with results
$response = $this->customise(array(
'Results' => $results ? $results->getResults() : '',
));
if ($results) {
$response = $response->customise($results);
}
// Use CustomSearchResultPage.ss template
$templates = array('CustomSearchResultPage', 'Page');
// Create a new CustomSearchResultPage page
$page = CustomSearchResultPage::get_one('CustomSearchResultPage');
// Build a controller using the CustomSearchResultPage
$controller = CustomSearchResultPage_Controller::create($page);
// Add the custom data to the newly minted controller
$result = $controller->customise($response);
// Return the controller and tell it how to render
return $result->renderWith($templates);
The page seems to render as expected just the variable is always empty...
Your explanation is a little hard to follow I'm afraid. So I'm answering for what I can ascertain as below:
Performing a search. This requires loading a controller to do as such.
Customising the current controller with the results
RE-customising the current controller with itself.
Setting the template for the current (double customised) controller.
Disregarding all of the above.
Fetching a random page (or an empty record).
Creating a controller for the empty page.
Customising the new controller with the customised controller of the current controller customised with itself.
Returning that page, which shows no results.
You need only stop at step 4 (skip step 3), and return the customisation ($response).
If there is some reason you think you need another controller however (which seems superflous, but who knows), creating and then customising that one (only) before returning it would be better.
Being that you have only used this second controller for rendering a result, the URL will not have changed or anything. The whole thing seems beyond requirements.
A much more simple way to render a result from this action would probably be:
return $this
->customise(['Results' => $results ? $results->getResults() : null])
->renderWith(['CustomSearchResultPage', 'Page']);
(from the top of my head, may need a little refining).

Yii2 - How to grab id inside layout

I am needing to know the ID of the page being viewed from within my layout. Actually, it's rendered within my Layout.
site.com/controller/view/id
or
site.com/controller/id <-- shorthand for the same above
I need to get that id number earlier, within my layout.
There is $this->context->action->id but that returns view. I don't see why it isn't possible for Yii to do this. If it doesn't exist, they should add it :)
I really don't want to chop the url, that's a chincy hack, and chincy hacks usually break later down the road for one reason or another.
You can simply get it from $_GET['id'] or with \Yii::$app->request->get('id');.
If you have some logic depending on controller and item id being viewed consider place it into widget, this will make your layout more readable
EDIT:
id is just a parameter name, to get it in layout you can use two approaches noted above or if you want to use context you can get it like this $this->context->actionParams['id']
You can extend your yii\web\View.php and add $id as a public property.[Or simply add public $id; in your yii\web\View.php]
Then in your view file,add
$this->id='watever';
and get the same in your layout as
<?php echo $this->id; ?>
The content is generated before the layout. Either way inside the controller before you call $this->render('view',['model' => $myModelVar, 'someotherparam' => $somevar']);
You would set the \Yii::$app->params array. This will be accessable inside the layout.
e.g. in your controller.
public function actionView($id) {
\Yii::$app->view->params['layoutvars'] = ['id' => $id];
return $this->render('view', ['model' => $model]);
}
inside your layout file
$id = \Yii::$app->view->params['layoutvars']['id'];

Laravel4 - Trying to create widget like partials that will pass/accept a parameter to get the correct data

Within my views I am trying to reuse a partials, so I can create a list of latest posts anywhere in the site / sidebar by using the blade #include where the passed variable is the number of posts to be included, like this:
#include('widgets.lastestposts', array('numPosts' => '10')
However the problem I have is how to get the Post data for the correct number of posts within the partial?
I could pass through a list of all posts via Post::all() using the controller or even a View::composer and then within the partial use a #for|#endfor loop to only show the correct number based on the 'numPosts' value.
However this doesn't feel right and I am sure there must be a better way than pulling a complete list of Posts when I may only need 5 or 10.
I tried View::composers but I could find how to pass through a variable so I can get the correct number of Posts returned. I can't access the parameter 'numPosts' via
$view->getdata()
as I expect 'numPosts' needs to be passed to the view via the controller, rather than the Blade file - either that or I messed up!
Am I missing something easy here or is what I am looking to do actually a very bad idea and I should be doing something else?
Any pointers are most gratefully received. Thanks!
(ps - I was looking to be able to do this via the blade file rather than setting up the number of posts in the controller to allow our designers/HTML coders to simply add the widgets and parameters to the views rather than have to mess with controllers.)
I would do this using View Composers. You can pass data to the composer with your include:
#include('widgets.lastestposts', array('numPosts' => '10')
and then from within the view composer you should be able to access that param like so:
View::composer('widgets.latestposts', function($view)
{
$view_data= $view->getData();
$post_count = $view_data['numPosts'];
//You will have to implement something to do this
$post_data = Post::getLatestPosts($post_count);
and then you can pass the post data back with:
$view->with('posts', $post_data);
}
and then from within your blade partial widgets.latestposts you can iterate over $posts to display the posts.
I know you said in your post that you tried this method, but I am fairly certain that this approach should work. Double check all your filenames, file extensions (.blade.php) etc...
Hope this works.
Using a view composer is pretty easy:
You can, for instance, store your number of posts in a Session var:
View::composer('*', function($view)
{
$view->with('numPosts', Session::get('numPosts'));
}
Or just hardcode them:
View::composer('*', function($view)
{
$view->with('numPosts', 10);
}
And use it in your view:
<?php $i=1; ?>
#foreach($posts as $post)
{{ $post->title }}
<?php
$i++;
if ($i > $numPosts) break;
?>
#endforeach
Assuming that you've passed $posts to your view:
$posts = Post::all();
return View::make('your-view')->with('posts', $posts);
But, remember, you also can do things like
$posts = Post::orderBy('created_at', 'desc')->take(Session::get('numPosts'))->get();
// or
$posts = Post::orderBy('created_at', 'desc')->take(10)->get();
// and
return View::make('your-view')->with('posts', $posts);
So you won't have to filter them again in your view.

storing a rendered element in a variable in CakePHP

I am having some trouble trying to "capture" the rendered html of an elmenet in cake php.
Say I have an element named "message.ctp"
I would like to do something like the following:
A making a $.getJSON request to an action in a controller say jsonAction(). Within this action I will perform some DB updates and return a json string. I would like to store the html is a part of the json object. Doable?
function jsonAction() {
//Do DB update
if(db update was ok) {
$response = array("completed" => true, "html" => $this->render("message"));
} else {
$response = array("completed" => false);
}
echo json_encode($response);
}
What seems to be happening right now is that the render method echos the rendered value instead of returning it.
Anyway I can achieve this?
Thanks
Regards
Gabriel
Forget elements for the time being.
First of all you have to separate everything that includes outputting stuff from the controller (either it is HTML or JSON or whatever).
For every controller action you have to have a corresponding view. So, for controller action jsonAction you should have a view names json_action.ctp (at the corresponding folder, e.g. if jsonAction is at MessagesController create a folder named /view/messages/json_action.ctp).
Set your variable from controller, display it at view and you are done. Do not forget to $this->layout = 'empty' from controller so that you display only what you have at the view.
Generally you should redo the CakePHP tutorials and reread the book it order to understand better the MVC (Model-View-Controller) pattern and CakePHP structure.
Do you mean this?
$myRenderedHtml = $this->element('message');
^^^^^^^

What's the proper MVC way to do this....?

Quick question about general MVC design principle in PHP, using CodeIgniter or Kohana (I'm actually using Kohana).
I'm new to MVC and don't want to get this wrong... so I'm wondering if i have tables:
categories, pages, notes
I create a separate controller and view for each one...? So people can go to
/category/#
/page/#
/note/#
But then lets say I want to also be able to display multiple notes per page, it would be bad to call the note view in a loop from the page view. So should I create some kind of a function that draws the notes and pass variables to that function from the note view and from a loop in the page view? Would this be the best way to go about it, if not how else should I do it...?
Thanks,
Serhiy
Yes, instead of just passing 1 entity (category, page, note) to your view, pass a list of entities. With a loop inside the view, you can display the whole list.
That view may call another one (or a function) that know how to display one entry.
I would personally have a "show" method for one item and a "list" method for multiple. In your controller you can say something like $page_data['note'] = get_note(cat_id,page_id) for the "show" method and $page_data['notes'] = get_all_notes(cat_id) for the "list" method.
Then in your view, you loop over the $page_data['notes'] and display HTML for each one. If the list view is using the same "note" HTML as the "show" view, create a template or function to spit out the HTML given a note:
// In your "list" view
foreach($n in $page_data['notes']){
print_note_html($n)
}
//In your "show" view
print_note_html($n)
The print_note_html function can be a helper method accessible by all views for Notes. Make sense?
You can loop in the View. The View is allowed can also access the model in MVC. See: http://www.phpwact.org/pattern/model_view_controller
You don't need to have a controller (or model) for each table.
In CodeIgniter I create a separate helper file where I put functions that return the markup for UI elements that may need to be included multiple times in the one view.
In your example, I would create a function to return the markup for a note.
application/helpers/view_helper.php
function note($note)
{
return '<div class="note">' .
'<h2>' . $note->title . '</h2>' .
'<p>' . $note->contents . '</p></div>';
}
I would normally auto-load this helper file. And then in the view I would do something like this.
echo note($note);
For a list of notes in a view, I would iterate the list calling this function.
<div class="note-list">
<?php foreach ($notes as $note) : ?>
<?php echo note($note); ?>
<?php endforeach; ?>
</div>
I found that including a view many times in another view was slow. Thats why I did it this way.
Edit
I just dug into the CodeIgniter Loader class and sure enough a PHP include is being done every time you call
$this->load->view('view_name');
This means that if you use this method to display a list of 20 notes, you're going to be doing 20 separate includes.

Categories