In the past I've used a proprietary framework that kinda followed an Objective-C like View Controller scheme.
Basically I was able to in one controller instantiate another and pass it some values like an array of products and then inject the reference to the controller to my view and render it whenever I wanted by issuing: $controllerReference->render();
This could be useful in many cases, eg. if I had a controller responsible for rendering a catalog, I would just pass it an array with all the items I would like to see and it would take take of pagination and displaying the items by itself...
Example:
At \UI\Homepage\Controller.php (the controller responsible for the homepage):
// Instantiate a ProductDisplay Controller
$controllRef = new \UI\ProductDisplay\Controller();
$controllRef->setProducts( ... ); // Inject the product array
// Load the current view for this controller and pass it a reference for the ProductDisplay controller
$this->loadSelfView(["controllRef" => $controllRef]);
At \UI\Homepage\View.php (the view loaded before):
// some html of the view
$controllRef->render(); // Render the ProductDisplay view here!
// some other html
How should this functionality be accomplished in Laravel? From what I've been reading Laravel tries to avoid this kind of actions, why? What are the workarounds?
Thank you.
Here is how I will do this, it only work if the called controller method return a View object like return view('home');):
// Get the controller class from the IOC container
$myController= \App::make(MyController::class);
// Execute the controller method (which return a View object)
$view = $myController->myControllerMethod($someParams);
// Return the View html content
$html = $view->render();
you can use the Controller.php class which is extended by all other controller to make a generic method in it to:
Get a controller instance
Call the right method with x parameters
Return the rendered content if it's a view class OR throw an exception (for exemple)
In recent versions of Laravel, it's possible to use Blade's #inject directive. It seems its main purpose is to inject services but I successfully injected controller actions.
Here is a snippet on what you have to do:
#inject('productController', 'App\Http\Controllers\ProductController')
{!! $productController->index() !!}
Just remember: since you're calling the controller method directly, and not through the router, you'll have to pass all required params. If that action requires a request instance, you may call it this way:
{!! $productController->index(request()) !!}
Probably the view returned by the called action contains HTML code. It's important to wrap the method call within {!! and !!}. Using regular {{ }} tags will cause the HTML code be escaped by the template engine.
Related
To build a sidebar that has a lot of dynamic data on it I learned about View composers in Laravel. The problem was that View Composers trigger when the view loads, overriding any data from the controller for variables with the same name. According to the Laravel 5.4 documentation though, I achieve what I want with view creators :
View creators are very similar to view composers; however, they are
executed immediately after the view is instantiated instead of waiting
until the view is about to render. To register a view creator, use the
creator method:
From what I understand, this means if I load variables with the same name in the controller and the creator, controller should override it. However this isn't happening with my code. The view composer:
public function boot()
{
view()->creator('*', function ($view) {
$userCompanies = auth()->user()->company()->get();
$currentCompany = auth()->user()->getCurrentCompany();
$view->with(compact('userCompanies', 'currentCompany'));
});
}
And here is one of the controllers, for example:
public function name()
{
$currentCompany = (object) ['name' => 'creating', 'id' => '0', 'account_balance' => 'N/A'];
return view('companies.name', compact('currentCompany'));
}
the $currentCompany variable in question, for example, always retains the value from the creator rather than being overridden by the one from the controller. Any idea what is wrong here?
After some research, I realized my initial assumption that the data from the view creator can be overwritten by the data from the controller was wrong. The creator still loads after the controller somehow. But I found a simple solution.
For those who want to use View composers/creators for default data that can get overwritten, do an array merge between the controller and composer/creator data at the end of the boot() method of the composer/creator, like so:
$with = array_merge(compact('userCompanies', 'currentCompany', 'currentUser'), $view->getData());
$view->with($with);
I'm writing a website using Slim Framework and Twig.
The sidebar on the website will have dynamically created links and i want to get the links from the database/cache every time a page is rendered, i can do this in each controller action but i would like to do this in my base template so i don't have to get and render the data manually in every controller action.
I have looked through the twig documentation and I haven't seen anything that could be of use besides the include function/tag but i don't see how i could get the data easily.
Is my only option to write an twig extension to do this or is there an easier way?
First of all: templates usually shouldn't contain any type of bussines logic but only render the data you provide it.
However you could write a custom twig function and use that to aquire the menu data from the DB in order to render it in your base template.
Alternativeley you could write some Slim middleware that aquires the data which might be able to inject the data into the template.
Hope that helps.
After reading Anticoms answer i was able to do it by writing a Twig extension.
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('render', array($this, 'render'))
);
}
public function render($template, $controller, $action) {
$app = Slim::getInstance();
return $app->$controller->$action($template);
}
Now i can simply write
{{ render('Shared/sidebar.twig', 'Controller', 'Index') }}
Into my twig template to render the template with the data i want.
Note that my render() function uses slim dependency injection to instanciate the controller at runtime.
I have created my own view helper according to this example. The view helper calls my model to create a form. The view Helper returns a form object.
My question is wether it is possible to assign a custom view script to the View Helper for the form. Or should i just use a partial for the script?
Thanks very much
View helpers should be used to help construct HTML that requires anything more that the normal control logic, the <?php if ($foo) : and <? foreach($foo....
You will want to avoid cases where you view logic becomes too complex or repetitive, as this results in hard to maintain code.
The example you posted, a <span> tag is returned based on the the number of days ($numberOfDays) arguments passed to it. This is something that is most likely repetitive to check every time you wish to output the 'new' item, hence the need for a view helper.
In your case, what would be ideal, would be to fetch the form via the FormElementManager from within the controller.
class FooController {
function someAction() {
$serviceManager = $this-getServiceLocator();
$formElementManager = $serviceManager->get('FormElementManager');
$myForm = $formElementManager->get('My\Form\Class\Name\Or\Alias');
return new \Zend\View\Model\Model(array(
'form' => $myForm
);
}
}
Calling the form from the FormElementManager will ensure its dependencies are correctly injected and init() called.
In the view you would then render the form using Zend's built in ViewHelpers (Like FormRow taking our complex form object and return all the required HTML)
With the above in place the need for you to 'assign view scripts to the form' disappears as the form can be injected, via the controller, into any view script.
I have a controller that is called with AJAX (sends JSON data), so I don't use a view.
I need to use a personnal view helper to format my data, but in my controller.
Is that possible ?
Or maybe I am doing it wrong (maybe I should have a view, but how with JSON) ?
You can access any ViewHelper from the Controller by
$this->view->helpername(/*params*/);
// or
$helper = $this->view->getHelper('helpername');
// or
$broker = Zend_Controller_Action_HelperBroker::getStaticHelper('ViewRenderer');
$broker->getView()->helpername(/*params*/);
See Zend: How to use a custom function from a view helper in the controller?
However, you might be right that you are doing it wrong (funny pic btw), but I cannot really tell from your question. Please refine it as to why you need to call the view helper and what it is supposed to format.
Zend_Controller_Front::getInstance()->getParam('bootstrap')->getResource('view');
Just be sure that the returned view is the view you want. Because down the line, the view may get overwritten and on the controller you have a spank new view.
And all those values you setup on the view on the action helper and the like... before the controller is kicked in? All gone with the wind!
So test before assuming that if you get a view resource. it is really the same view resource you expect, and that all your vars are still there.
You may be surprised as i was!
You can create an instance of a Helper .. this will work in Controllers, Models and everywhere you need the Helper.
eg.
// create Instance
$serverUrl_helper = new Zend_View_Helper_ServerUrl();
// get the ServerUrl
$serverUrl = $serverUrl_helper->serverUrl();
Another approach is to use the ContextSwitch or AjaxContext action-helpers. This allows you to use a view-script from which you can then call your view-helper in the standard manner.
Just use action helpers, many of view helpers are available as action helpers too.
Or directly by using Zend_Date or sprintf.
I think I can't see the tree in the wood.
I'm using Zend Framework, with an layout.phtml which is rendering and partial
<?php echo $this->partial('_header.phtml') ?>
My goal is to render an form from my IndexController into the "_header.phtml" with
<?php echo $this->form; ?>
How can I pass the form to the partial view?
View partials are rendered with a clean variable scope... That is, they do not inherit view variables from the calling Zend_View instance.
There's a few options available to you here:
One, simply call:
echo $this->render('_header.phtml');
instead of using a partial. This file will have access to all your view variables, so you can just assign the form to your view in your controller, like anything else.
Another way is to explicitly pass your form as a variable to the partial, like so:
echo $this->partial('_header.phtml', array('form' => $this->form));
// $this->form inside your partial will be your form
Your other option is to either use placeholders, or layout response segments. Here's an example of placeholders:
In your _header.phtml, or layout... where ever you want the form to render:
<?php echo $this->placeholder('header'); ?>
And in your controller:
$this->view->placeholder('header')->append($form);
// I'm not sure, but you _may_ want to pass in $form->render() here.
// I can't remember if implode() (which is used in placeholders internally)
// will trigger the __toString() method of an object.
This has the added bonus of not polluting your view instance with one-off variables, like the form.
Note: I'll link to the manual pages as soon as the ZF site is back up; 1.9 launch is today, so the site's getting updated currently.
Here's some relevant manual pages:
Placeholder view helper
Partial view helper