Laravel make multiple routes to have common parameter - laravel

The question title is confusing I know, cannot put better way to describe my situation... I have quite some routes for ecommerce website, and I'm using blade templating system so that there is a header.blade.php that is being used for all my page of this site. In the header, there's a shopping cart icon, when hovers on it, all the products that are in the shopping cart for the current user would show up. Problem is with all the routes that goes to some page that contains this header template, I have to add
$shopping_cart_info = ProductController::getShoppingCartInfo();
return View::make('somePageView')->with('shopping_cart_info', $shopping_cart_info);
for each and every one of them in the corresponding controller function, and there are more than 50 routes that require to do so... Is there a way to group these routes to let them all have the $shopping_cart_info so that I don't have to add it one by one?

Sure thing, except you don't do it in the routes. It's called a view composer, and it works like this:
// adds shopping cart info to the view
View::composer(['some.view.1', 'some.view.2'], function($view){
$shopping_cart_info = ProductController::getShoppingCartInfo();
$view->with('shopping_cart_info', $shopping_cart_info);
});
Now, the views "some.view.1" and "some.view.2" will both contain the shopping cart info variable. You can have as many views as you'd like there. In your case, it sounds like you have one view which uses it but which is called from loads of places. View composers were created just for that sort of thing! :)
For a smaller project, I typically create a file app/composers.php and autoload it from the app/global.php file like so: require app_path().'/composers.php';
Documentation: http://laravel.com/docs/4.2/responses#view-composers

Related

Laravel 8: How to share a PHP variable with different pages (using same layout) and components?

I am working on this web app (Laravel 8) in which users can choose their color themes. Basically, to make it simple, let's say each user can choose their primary_color.
On home page (which does not need authentication), the primary_color will come from config (whether user is authenticated or not). On authenticated pages, the primary_color comes from the users table (for the authenticated user). I also have a bunch of pages that use the guest layout if user is not authenticated, and the app layout if user is authenticated. Those would use the primary_color from config if user is not authenticated, and the primary_color chosen by the user if they are authenticated.
All those pages use some components (buttons, nav-links, etc) that need to know the primary_color.
I currently have this line on top of EACH php file that needs to know the primary_color:
$primary_color = (Auth::user() && Route::current()->getName() !== 'home') ? Auth::user()->config->color_theme : "yellow";
(Color "yellow" is hard coded here, just ignore it.)
I would love to avoid that much code replication, but i can't seem to find a way to share this variable from the layout (guest.blade.php or app.blade.php) to the page. I can't seem to be able to include a style.blade.php view that contains the variable, either. The variables declared in this included view do not exist in the rest of the page. I managed to pass the variable to components using a prop. It's not so nice, but it works fine. But it's not so nice, i'd rather use a cleaner solution.
But most importantly, how do i avoid having to copy the same bit of code on top of each page?
Thank you!
You can use view composers. Under the boot method of AppServiceProvider add the folowing code.
\Illuminate\Support\Facades\View::composer('*',function($view){
$userPrimaryColor = (Auth::user() && ! Route::is('home')) ? Auth::user()->config->color_theme : "yellow";
$view->with('primary_color', $userPrimaryColor);
});
The first argument * indicates that it needs to be shared to all the views. If you need those in few pages you can pass the list of pages as array
If you want to share data with all the views you can use laravel's view composer.
it can attach data to all view or multiple views simultaneously.
source: https://laravel.com/docs/5.0/views#view-composers
alternately: see here
https://www.digitalocean.com/community/tutorials/sharing-data-between-views-using-laravel-view-composers

Joomla Terminology: view, layout, task and component development

I'm a developer and I am very confident with MVC pattern and have already developed a lot of webapp from scratch using php framework like symfony or yii.
I'm a little bit confused about joomla mvc and terminology and after googling a lot, read joomla book extensions guide, read on joomla website my doubt are still there.
What is confusing for me is the component mvc structure and how I have to set up "my way of think" about joomla mvc, for doing the things in the joomla way.
In particular I am used to reasoning in terms of controller/action (like in symfony and yii framework)
So the final list of all my webapp url will be
controller1/action1
controller1/action2
controller1/action3
controller2/action1
controller2/action2
Each controller's action will decide what view to render and what layout to use for showing the view.
In particular in these frameworks, the definition of a layout is exactly the mean of a layout. And the view is the "core part" of the page.
So I can have a view with a list of users and I can put this view inside a mobile layout or a desktop layout, or to build a view for mobile and put it only in the mobile layout and so on.
The final result about directory structure in my webapp is something similar to the following:
controllers/
controller1
controller2
controller3
models/
modelForTableA
modelForTableB
views/
controller1/
viewForAction1
viewForAction2
layouts/
mobileLayout.php
desktopLayout.php
and for me is very clear to understand.
So finally my questions are:
how would be the directory structure in joomla?
what are in joomla the definition of view, layout and task?
I need to clarify that I do not need an explaination about MVC works in general, but if I would achieve the same result as before, how I have to organize my code and my work?
Suppose that I want to build a component with the following "url"
userController/addUser
userController/editUser
userController/listUsers
userController/viewUserDetail
anotherController/addOperation
anotherController/editOperation
anotherController/myNonCrudOperation
Thank you very much
Routing in Joomla is slightly different. The SEF URLs are built from menu items, which in turn point to a View/Layout combination.
This turns things around: a controller is not bound to a specific View/Layout.
Let's make an example of the flow with the addUser functionality you mentioned as an example; I'll be referring to these files (but you'll have plenty more):
/controllers/user.php
/models/user.php
/views/useradd/view.html.php
/views/useradd/tmpl/default.php
/views/useradd/tmpl/default.xml
/controller.php
/router.php
As you can see the layouts are inside each view's tmpl folder.
router.php
Let's start from this last file: router.php defines our custom SEF rules so, after Joomla passes the call to our component (usually with the params
?option=com_componentname) we can takeover and interpret the URL as we wish. It is a bit hard to get started with but does provide the most flexibility and power. We don't really need to implement it at all for this simple example: so back to our registration now.
First step: show the "new user" form.
You would typically bind this to a menu item, pointing to the /views/useradd/tmpl/default.php; the /views/useradd/tmpl/default.xml contains the definition of the layout so it's available in the menu manager. Very often there is only one layout per view.
Control is passed to the view /views/useradd/view.html.php , and the view will then load an instance of its own model (automatically chosen based on the view name, you can load other models of course) to gather any initialization data.
The view then renders the layout, and presents it to the user.
The layout's responsibility includes generating a form with an appropriate action (endpoint) and security tokens if appropriate:
<form action="index.php?option=com_mycomponent">
<input type="hidden" task="user.save">
<?php echo JHtml::_('form.token');?>
as you see it doesn't really matter if you want to use <input or params on the url, and you can most often mix them.
Form interaction
For autocompletion the form may need to invoke some backend controller methods, i.e. the method emailAvailable() in the /controllers/user.php
It does not make sense to have such functionality indexed, so we'll invoke the method directly with a non-SEF url:
index.php?option=com_ourcomponent&task=user.emailAvailable
followed by any other parameter. This will work in both get and post.
The controller /controllers/user.php's emailAvailable() method will return a json structure and then invoke exit() as we don't want the CMS to kick in at all. An alternative solution is to add the param &format=json in the call.
{"email":"johndoe#example.com", "available":true}
Saving the data
When the user submits the form, processing is first handled by the controller since a task is specified. (see above task=user.save). Joomla will invoke the method save() in the controller /controllers/user.php.
This time, however, our controller is responsible for returning information to the user. After processing the data, it may choose to re-render the registration form showing an error, or a thank you page. In either case the controller simply sets the redirect, letting Joomla handle the rendering when appropriate.
$this->setRedirect(JRoute::_('index.php?option=com_yourcomponent&view=useradd', false));
More control
Each time a controller task is not specified, the display() method of the main controller is invoked. You can add custom logic there.
Joomla fires several events during a view rendering; these can be intercepted by a system plugin or - if you add in the calls - other kinds of plugins as well. You may even create your own types of plugins. Do not try to instantiate a view manually from a controller, as this may inhibit plugin firing.
Small insight,
1) Directory Structure
controllers/
controller1
controller2
controller3
models/
modelForTableA
modelForTableB
views/
layout1
2) View and layout and task
check this answer
3) More routing techniques with SEF.
Hope it helps.
solved with this. I cannot delete this question because there already exists other answer.
Could any moderator close or delete this? Thank you
https://joomla.stackexchange.com/questions/18774/joomla-terminology-view-layout-task-and-component-development/18799#18799

How to make a laravel 5 view composer

I'm still learning Laravel and I'm working on a small project to help me understand better. In the project, I am in need of a global array, so that I may display it or its attributes on every view rendered. sort of on a notification bar, so that each page the user visits, he/she can see the number of notifications (which have been fetched in the background and are stored in the array).
I have done some research, and realized that I have to fetch and compile the array in a view composer I think. But everywhere I go, I cant seem to understand how to make a view composer.
I need to fetch the relevant rows from the database table, and make the resulting array available to each view rendered (I'm thinking attaching it somehow to my layouts/default.blade.php file.). Please help, any and all advice is greatly appreciated:)
You can now inject services on your view
More info here: https://laracasts.com/series/whats-new-in-laravel-5-1/episodes/2
You have to use Sub-Views of laravel blade. I guess your functionality is like a sidebar or like a top bar which will be rendered at every page.
//Your Controller pass data
class YOUR_CONTROLLER extends Controller {
public function index()
{
$data = YOUR_DATA;
return view('YOUR_VIEW_FILE', get_defined_vars());
}
}
//In Your View File
#extends('LAYOUTS_FILE')
#section('YOUR_SECTION')
#include('YOUR_SUB_VIEW_FOR_NOTIFICATION')//You need not pass any data passed all data will be available to this sub view.
#endsection
In your sub view
//Do what ever you want looping logic rendering HTML etc.
//In your layout file just yield or render the section that's it
#yield('YOUR_SECTION')
More explanation can be found Including Sub-Views

Zend_Acl For Specific Parts Of The Page

I know how to use Zend_Acl to allow certain resources to certain usergroups but how do i use it to allow only specific parts of the page to be shown? For example
I have a button to delete a post via AJAX on the page but i only want to show the button to Admins only. I can use Zend_Acl to block access to the controller post/delete but i can't use it to block the button from showing.
// in controller
$this->view->allow_delete_post = $acl->isAllowed($role, 'delete_post');
.
// in template
<? if ($this->allow_delete_post): ?>[button html]<? endif; ?>
Would that not do it?
You can also write a custom static ACL class which you can then call directly from within your view script.
Since ACL is normally handled at plugin level it means that if your visitor is seeing the view then ACL has already allowed the resource, therefor inside your view you can now do something like this...
if(My_Custom_Acl::getIsAllowed('some_resource', 'delete_post_action'){
I did not specify the role name in the custom getIsAllowed() method, because at this point ACL is already suppose to know the user's Identity and the Role.
Hope this helps
Although Christof gave a good solution, an alternative is to split the views. Although this starts to violate DRY, when you have about 200 different admin things/controls, it's getting heavy in the view - thus splitting the view with $this->render('view') and $this->render('edit') for permissions from the controller is sometimes easier. Then only the edit view script has the edit links. But again, it's DRY, so not optimal, but an alternative. I guess you have to weigh it up, which one is more DRY, repeating the ACL check or the stuff in 2 views...

HMVC and dynamic widgets

Background
I use the word widget as a partial view that have its own controller (so its own actions) and it is placed in almost all pages. I implement the rendering of this via HMVC, that is just great.
Problem
Now, the problem is that the widget itself execute actions. Think about a shopping cart widget. That widget is placed in all pages so the user can see his/her stuff all along. And the widget have actions that are related to it, for instance: RemoveItem, RefreshCart, ApplyDiscountCoupon, etc. Such actions should trigger by a button or link and the link should be something like (in HMVC):
<a href='<?site_url()?>/cart/cart/removeitem/the_item_id'>Remove this item</a>
Ok. Now the user clicks that link, the cart module and cart controller are loaded and the action is executed, the action should look something like:
function removeitem($itemid)
{
// remove the item from db
...
// "load the view" ???
}
As you can see, my question is how to load the view in a HMVC module. The thing is that if I only load the cart view, it will only show my cart, and I can’t just redirect or load the main page because it can be any main page, that is: the cart could be in any main page (select product, keep buying, see product details, billing info, checkout, etc). :/
Another thing: a requirement is that I can’t use AJAX in this project.
Do you know how HMVC handle this?
Thanks in advance.
Ok. No luck with the community. Nevertheless I found a workaround. Hope would be helpful to someone.
HMVC doesn't have a natural solution to this. So I decided to use this workaround:
Into each main controller (that is, no widget, no partial view) I grab the current url in session this way (/controllers/keep_buying.php):
class Keep_buying extends Controller
{
function Keep_buying()
{
parent::Controller();
$this->session->set_userdata('main_uri', uri_string());
}
...
}
Then in my partial view widget (HMVC module view) I have a normal link to my widget controller (/modules/cart/views/cart_show.php):
<a class="button" href="cart/cart/additem">Add Item</a>
At the controller action (HMVC module controller action) I retrieve the current main page, do stuff and then redirect to that page, that implicitly will get into my widget flow (due to HMVC).
class Cart extends Controller
{
...
function additem()
{
$to_redirect = $this->session->userdata('main_uri');
// add-item work stuff...
redirect($to_redirect);
}
}
That is. Is not the ideal approach IMHO, but works.
I know that this topic is over a year old, however I have been Googling to gain some more insight into the structure an HMVC program should take, and how it can be implemented in CI, and have not found very many answers.
I'm pretty sure that for HMVC to work properly, with several widgets incorporated on all pages of a site, they need to be orchestrated by one main controller. This will be the top level of the heirarchy. Let me try to give an example:
You have a site with a shopping cart widget on every page. When you wish to use the cart functionality, you will need to link to methods in the cart module, e.g.:
add
The problem using this link is that you do not want to reload the rest of the site from that module. Instead, you can route the link through your main controller and have it reload the module, calling the desired method. e.g.
add
Then in the site controller, it will simply pass the method call to the main view
<?php
function cart_add($item){
$data['cart'] = 'cart/add/'.$item;
$this->load->view('main', $data);
}
?>
The view will call the cart widget with the following:
<?php echo modules::run($cart); ?>
Obviously this is a simplified explanation, and the main site controller will be handling several widgets in one main view, so passing the correct data will need to be managed better than just calling the main view from within a method. But hopefully this gives an idea of the structure I am referring to.
If anyone reads this and thinks I'm wrong, I would love to hear other solutions people have found to this issue.

Resources