I would like to find out which strategy is the best for links, forms and DOM elements appearance manipulation (show/hide) on top level of multi modular application (admin, default, etc) ??
It should be done through ACL and it's usage I know well, permission are stored in DB.
If someone tries to access certain page (module/controller/action) that is not allowed for him, an info page is passed which says that he is not allowed. In that case all elements are visible.
I have a few strategies for elements appearance manipulation on my mind:
To set in controller
$this->view->allow_delete_link = $acl->isAllowed($role, 'delete_post'); // boolean
and to ask in view file if it is true or false and show/hide
<?php if ($this->allow_delete_link): ?>[link html] <?php endif; ?>
The drawback here is if i have 50 links on page I will need to have 50 lines of code in my controller where I am doing this and I don't like that very much.
Similar to the first except ACL is static class so view file asks if:
<?php if(My_Custom_Acl::getIsAllowed('some_resource', 'delete_post_action'){ echo 'link'; } ?>
To make one view helper which I will call upon creating every link in which I would ask if user that is logged in has access, if yes return the whole link, if not, return "" or false.
View helpers are very slow so that's my last resort.
To make separate view.phtml file for every group of users, then in controller, in which user is logged, show appropriate view.
$this->render('xx_view');
This violates DRYS, so I think this method is not good.
Is there some other strategy for something like this, because I already see that I will have headache if I choose any of these 4.
Maybe some existing plugin/class for that would be the best?
thanks in advance !
I forgot to mention, that I am using Zend framework and Zend_Acl.
3 is the best solution, because you often need to pass the database row to the isAllowed call (for example, if you need to test the owner of the post to choose if you can show the delete link).
You can't do this in the controller like you suggest in 1, because you will need one line for each row.
2 is ugly.
You can speed up resolution of view helpers by extending Zend_View: http://framework.zend.com/manual/fr/performance.view.html.
Related
I'm working with MVC (php) for a tiny website.
For the moment, each view is included from the controller, but just one HTML file per controller.
To begin to optimize (I think), I want to cut some files in two/three parts :
header.php (just html headers).
Headband : this part is my problem, because if a user is connected, I want to display user informations, if he's not connected, I want to display "Register/Login" links. Am I forced to create two different files for this headband ? Is it really a good practice ? it seems not to be a proper way...
If you know some best practices or exemples, thank you.
Footer.php, not important.
I'm certainly no expert when it comes to MVC architecture, but I believe that you should have some logic in your controller to determine whether you need to display the register link or whether you need to display account info. You'd save this in a variable. Then, in your view, depending on the value of the variable you've set, you display one or the other, which can be included as other views themselves.
A bit of pseudocode:
Controller:
if (user is logged in) {
display = 'register link'
}
else {
display = 'account info'
}
View:
print display
I have code that I want to run on every page load, such as looking up menu items, looking up the users details etc. These will be displayed on partial views that make up the main view.
Where do I place this code so that it can fill my partial views with each page load? I know I can just add the code to the top of the partial view itself, but this doesn't really follow the MVC pattern.
Is there a function that is always called that I can hook into in my base controller?
You can create a base viewmodel for the repeated code and make other viewmodels inherit from it.
...such as looking up menu items, looking up the users details etc
You're a bit unclear about the type of information you want to load: in case the info is a view-component then indeed you should create a base-view and inherit from it or include it (composition) in any other view.
But, in case it is "user-information" - the data should live in a model-component that again, may live as "base-model" object that is included in other model components.
I would like to ask about optimal approach to creation of reusable content blocks in CakePHP 1.3.
Under reusable content blocks I mean partial views used to build up the page. E.g. in an eshop application the minicart summary can be considered as reusable content block - it is displayed on each page header.
In CakePHP 1.3 there are two posibilities (and both have serious disadvantages):
Creation of element /app/plugins/myeshop/elements/minicart.ctp and use $this->element('minicart) to load this reusable content e.g. in header. The disadvantage is that this beaks business of Cart entity into many places. Minicart is only some representation of Cart entity whose views are treated by controller /app/plugins/myeshop/controllers/carts_controller.php. So why to put it out of controller and keep it in element? Elements are good to keep some general reusable contents e.g. header, footer, interactive_map, ... something which is not related to application business objects/entities.
Creation of method CartsController::minicart() with corresponding view and use $this->requestAction('/myeshop/carts/minicart') to load this reusable content e.g. in header. The advantage is that now all business and views of Cart entity is treated by CartsController. There are no side-logic and side-views hidden in elements.
The disadvantage of this approach is apparent - use of requestAction() costs a lot of time.
At this point I must say that I totally agree that requestAction() must be used very carefully. To use it to call some procedural/busines logic of controller is bad application design. Such kind of logic should not be placed in controller but in model. Still, IMHO, it is legitimate to call controller action to get a partial/reusable content (view) and keep the entity business on one place.
Does the CakePHP have some optimal solution for this?
I highly suggest going the route of elements/requestActions.
Here's a great article by CakePHP Master Mark Story: "How using requestAction increased performance on my site"
We've built many large, highly-traffic sites using CakePHP and use requestActions all over, and our sites load extremely fast.
If possible I would create it as an element. I assume your controller will calculate the contents of the mini cart and the view will display it?
If so its fine to use an element. You certainly dont want additional HTTP requests as you state.
However, the element should only display the content, it should perform any business logic. You need to incldue this in a Controller or Component and set the necessary elements for the view.
If you only need it on certain pages, you can create a new layout.ctp file that includes the mini cart and then use this layout on those pages.
If this is just about displaying the content of your cart on every page I recommend this solution: Write the cart into session, if the user is not logged in and logs in sync it with the database. If a user comes back and logs in restore the session from the saved cart.
In your AppController::beforeFilter() read and set the session data to the view or if it is just read, read it using the session helper in your minicart element. You won't have additional requestAction() calls by this or any additional db queries.
I've implemented my cart like this, you can take a look at it here. https://github.com/burzum/cart a working example app is also available https://github.com/burzum/CartSampleApp
I'm new to the MVC pattern but have been trying to grasp it, for example by reading the documentation for the CakePHP framework that I want to try out. However, now I have stumbled upon a scenario that I'm not really sure how to handle.
The web site I'm working on consists of nine fixed pages, that is, there will never exist any other page than those. Each page contains something specific, like the Guest book page holds guest book notes. However, in addition, every page holds a small news box and a short fact box that an admin should be able to edit. From my point of view, those should be considered as models, e.g. NewsPost and ShortFact with belonging controls NewsPostController and ShortFactController. Notice that they are completely unrelated to each other.
Now, my question is, how do I create a single view (web page) containing the guest book notes as well as the news post box and the short fact? Do I:
Set up a unique controller GuestBookController (with an index() action) for the guest book, so that visiting www.domain.com/guest_book lets the index action fetch the latest news post and a random short fact?
Put static pages in /pages/ and in let the PagesController do the fetching?
< Please fill in the proper way here. >
Thanks in advance!
It sounds like you need to look into elements, or else you may be able to embed this into the layout - but its neater to use an element if you ask me, keep the things separate.
http://book.cakephp.org/2.0/en/views.html#elements
These allow you to have create small views that you are able to embed into other views.
You may also need to put some logic into the AppController (remember all other controllers extend the app controller) to load the data required for these views. The beforeRender function should be useful for this - its one of the hook functions cakephp provides, so if you define it on a controller, its always called after the action is finished before the view is rendered.
Something like this in your AppController should help:
function beforeRender() {
$this->dostuff();
}
function doStuff() {
// do what you need to do here - eg: load some data.
$shortfacts = $this->ShortFact->findAll();
$news = $this->NewsPost->findAll();
// news and shortfacts will be available within the $shortfacts and $news variables in the view.
$this->set('shortfacts', $shortfacts);
$this->set('news', $news);
}
If there are models you need in the app controller for use within this doStuff method, then you need to define them within uses at the top of the AppController
class AppController {
var $uses = array('NewsPost', 'ShortFact');
}
I'm working on building up an interface that I want to function as a "tabbed browsing" sort of function. Each of these tabs has already been written as an action and the tabbed interface works fine as links to the individual tabs. I decided to try writing the "index" page for this controller - putting the content of all the tabs into hidden divs and swapping between them with jQuery, but once I started to use the action view helper - I ran into a lot of people saying that its bad practice. (see this article)
Some of these actions build up forms - grab some data from the model, etc to display. I want each of the actions to continue to function on their own (some parse forms as well).
Browsing to /item should give you the tabbed menu, plus all of the div's contents in a hidden tag - where /item/tab2 is a specific action (form submit for instance).
Another complication/caveat - Some of the actions will throw Access Exceptions if the user doesn't have access to that "tab". I'd prefer not to build access checking into the system twice (thus showing a tab with empty content).
I'm just trying to figure out what the best practice is to handle this sort of thing, and I thought that the action helper might be it. If I try to use View Helpers - I start wondering if that is the right place to assemble a Zend_Form.
Does anyone have any suggestions on the "proper" way to work around not using the Zend_View_Helper_Action ?
The Correct way to work around the action view helper, as I stated in the article you cited, is to create partials which access the model directly to fetch the data they need. This can be through a view helper (you make this yourself ;)) if this would involve a lot of logic in your view.
The action view helper is plagued with more than just performance issues, but also creates horrendous debugging nightmares, and if you need it, then your application is probably not following MVC, and therefore, you are using the controller for reuse, instead of the model, which is the patterns intention.
You can render partials from within your layout or current actions view with the render or partial methods of the view object.
If you have multiple actions to deal with multiple possible posts from your tabs, then you should set all of these actions to render the same view script, which will then render all of the tabs with data direct from the model.
If you're not generating the tab/tab panes from existing markup, and you're loading the content on demand, then you simply must check whether the user has permission to access the tab before displaying the tab itself, and again when attempting to load the tab's content.
Checking whether the user has these access permissions should be an acceptable mode of operation and should not be expensive to perform.
If these actions produce content that works in some standalone page, in addition to the tabs, then the Action view helper is the corrent way to proceed. Simply perform the same ACL (or other) check performed in the action when generating the tab.
I'm not entirely sure what your exact problem is, however you can disable the layout:
$this->_helper->layout->disableLayout();
Then the requested Action will just display it's view script, which you can load into the tab.
Any authorisation code you have will function as normal and you can display the requested view script for the Action, or not depending on if they have access.
You can catch any access exceptions by using a try/catch block:
try { // action throwing exceptions } catch (Exception $e) { // catch silently }