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 }
Related
Okay, I'm learning all about MVC, Bootstrap, Ajax and Smarty and I understand the basic principles of MVC. I am having one major issue however and I just cannot wrap my head around it, no matter how much I try and no matter how much reading I do.
At it's very core the thing I cannot seem to get my head around is how to pass variables from view to controller from controller to view.
If I want to assign a variable, I can simply $view->assign('variableName', 'variableValue') No issues there, then in the view if I want to call it, it's as simple as $variableName and it's in the view.
My issue is, I want to be able to minipulate data, for example let's say I want to have a list of items, numbers for the example, a list of 1-10, the user chooses 6, I want a way to be able to "POST" that back to the controller without actually having use POST/GET, I want to be able to essentially let it call an Ajax to send the users selection but I do not know the best way to do so.
If I was doing this without MVC, or Smarty it would be as simple as form, action post, I know that but unfortunately that isnt something I can use in this instance.
Any help you can offer would be appreciated.
I will begin with the normal workflow, e.g. without the MVC approach.
Let's say, in a certain moment you are seeing a web page in the browser and let's call it MAIN PAGE.
When you are submitting a form from main page without using ajax,
the whole page refreshes, no matter if the form action points to the
main page or to another one.
When you are submitting values using an ajax call (as part of the
main page code), then the main page will not be reloaded. E.g. an
ajax call targets ANOTHER PAGE to fetch some data in some format
(html, json, etc) and prints the data on screen, in a specified
container inside the main page.
Now, let's see what happens in a web MVC architecture.
You must understand, that an MVC application consists of only one page: index.php. This page serves as the MAIN PAGE, but ALSO as the ANOTHER PAGE, targeted when using ajax calls. The index.php page is therefore processed each time when you are sending a request to the web server - be it through manually changing the url in the address bar of the browser, through posting a html form, or through starting an ajax script.
All other components of the MVC structure (classes, template files, etc) are serving only one purpose: to build the structure of the index.php page - as main page or as ajax response page.
So, in principle, in the index.php page you'll have something like this:
Read the URL, e.g the url components: controller name, action name, action parameters (HTTP GET query string). For this you can use an instance of a Router class.
Create an object of type Request, passing and saving the url components into it. Here are read and saved the other server request variables too, e.g. HTTP POST, HTTP cookies, etc.
Create an instance of the View class.
Based on the url's controller name instantiate the corresponding Controller class, passing the Request object and the View instance to it. Here you'd give the model layer constructs (like a model factory object) as constructor parameter(s) too.
Based on the url's action name call the corresponding controller method, e.g. the controller "action". Exactly here, inside the controller action, are taking place the processing of the server request variables (saved in the Request object), the loading of the template files and their rendering, including transferring the processed server request variables into them.
In the last step, the rendered template files will be directly printed or further passed to an object of type Response, which in turn prints them.
In the end, you'll have a fully "constructed" index.php page, which will be either printed on screen by the browser (if index.php has the role of a main page) or processed by the browser as the result of an ajax request.
Note that I used the description of the steps found in the classical MVC approach. There are also other... types of this concept, best presented in Architecture more suitable for web apps than MVC?
Other very good resources:
MVC for advanced PHP developers (contains a list of great resources)
How should a model be structured in MVC?
Understanding MVC Views in PHP
Understanding MVC
MVC (Model-View-Controller) in PHP tutorial (Part I...IV)
Model-View-Confusion (Part I+II)
James Mallison - Dependency Injection and Dependency Inversion in PHP
Good luck.
I have a situation where I have several ways to perform the same activity in my php web app.
There is the "Manage Widgets" section of the app which has a form for creating new widgets and a list of existing widgets.
Then somewhere else in the app there is a button that pops up a dialog to add a new widget.
Then on the home page of the app there is another place where a form is embedded to add a widget (think home page portal).
My question is: What is the best practice for this? In this case all of the forms will be essentially the same. So my first instinct is to use the same code for all three of these scenarios. On the other hand, space on the home page could be smaller and layouts may have to differ between the three.
So even though it would be repetition, is it better to duplicate this form 3 times (there is a proper model layer, so the duplicated code would not include the logic to add/edit the widget)? Or try to force a single view in all of these scenarios? Both seem wrong to me and I am hoping for some ideas to discover some sort of middle ground.
One approach would be to have the markup (not the styles) for the form as a standalone file, which can then be included from anywhere you like.
You can then use AJAX to submit the form to a specific PHP script that handles the form submission and returns a meaningful JSON response. You can then display and style this JSON response on the page in question.
This way you have a single form (that can be styled differently) and a single handler for any view that's required to use the form.
i mean, the best way is compose form from other forms (Dont repeat yourself). You can use different templates for same form to change appearance of final form.
For example/idea you can check forms what is used in Nette Framework (http://doc.nette.org/en/forms)
T.
If you are just changing the styles, not the markup, I think the best approach is to add a specific class to the form element and then use Javascript (not Ajax, justa Javascript) to alternate between these clases as you need.
If your application do not use Ajax at all and you just generate web pages with PHP, is a simple matter of decide which class you form shoud have.
In CSS, you do something like this:
form.main { ... }
/* main form rules */
form.other { ... }
/* other form rules */
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
As I have understood, the main design idea of templating engines like Twig is to remove all PHP code from views, and to have the controller that renders a view have set all parameters that are necessary in that view.
However, imagine that a view consists of different "blocks": a header, a footer, a shopping cart and a productlist. You could have the order_controller.php check whether or not the customer is logged in (because if not, the header doesn’t contain the link "Log off"), as well as fetch a list of all available products (to show them in the product block), as well as fetch the contents of the shopping cart in the $_SESSION (to show them in the shoppingcart block).
However, it might be more interesting to have the order_controller only fetch one thing: the list of products. The view that will be rendered by the controller would then contain different includes for the other blocks (header, footer and shopping cart), but they wouldn’t include their views. They would include other controllers (showheader_controller, showfooter_controller and showcart_controller), which in turn would render their own single blocks (showheader_controller would only render the header-view etc…). In short: you would include controllers that render views in the main view. The logic that would check whether or not the customer is logged in would then be showheader_controller, for the simple reason that the header view is the only view that needs to know this. The logic that load the contents of the shopping cart from $_SESSION would be the showcart_controller, because the cart-view is the only view that needs to have access to this data, etc...
This way, you could have tons of controllers that all show the header, but the instead of having each controller repeat the logic to check whether or not the customer is logged in, you would only have it in one place (the controller that renders the header-view).
If there were a Twig function to include any external source on that spot in the view, the problem would be fixed (since I could just include the other controllers), but as it is it only allows for the including of other templates (that cannot have PHP-logic in them, otherwise it would kinda defeat the purpose).
The way I fix this now is to have the order_controller have this logic:
Fetch the productlist
Start the buffering of the outputstream (ob_start())
Include the controller that renders the header (showheader_controller)
Store the contents of the buffer in a variable $headerView.
Clean up the buffer
Start the buffering of the outputstream (ob_start())
Include the controller that renders the shopping cart (showcart_controller)
Store the contents of the buffer in a variable $cartView.
Clean up the buffer
Rinse and repeat...
In the view, I print the contents of the set variables:
{{ headerView | raw }}
The raw-filter has to be added, because the headerView variable contains html-tags.
It works perfectly, but it’s not exactly “neat”.
My question: would there be a better way to achieve this strategy?
First, this can be simplified by implementing the MVC separation better. The controller's job is pretty much solely to react to events and poke the model into doing appropriate things in reaction to this event. It is not the controller's job to hold hands with the view, nor is the view entirely dependent upon the controller.
The model is your core application, modeling all the things your application is supposed to do. The view's job is to visualize what is going on in the application, to give the user something to interact with. The controller is just a little glue between the user and your application, something that lets the user control the application.
As such, the controller reacts to events like "user visited homepage", triggers necessary events in the model, which causes the view to update to represent the new state of the application. The view can do whatever it needs to do. The view is not just a Twig template. The view can talk back to the model to get more information. In the end it is not the controller that needs to send necessary data to the Twig template, it's the view that needs to set the necessary data for the template before it renders it. Less code in controllers, more code in the view.
If you want to make this more modular, you can define custom Twig tags or functions which may fetch the necessary data from wherever it needs fetching. For instance:
<div class="head">{{ user_login_status() }}</div>
user_login_status is a Twig extension function which can talk to the model to get the data necessary to display the status.
Hope you get the idea.
I have a line of code I wish to run after a view script has rendered, but I want this code to be placed in the action.
This is action specific and only updates a flag in the database, so it seems like overkill to utilise a plugin just for this.
What other options do I have? Could I register an abstract plugin that I can extend and hook into dispatchLoopShutdown() or routeShutdown() from the specific action I am in?
Thanks
By default 'View' is the last thing that the pointer will pass through.
you can:
$this->_helper->removeHelper('viewRenderer');
to globally remove views and then do the rest of action.
Actually, in many of my codes, I load views in chunks and have not faced any issues whilst rendering them before. Example, if I do:
$this->view->shouldGoinHeader;
// some actions here
$this->view->someHere;
// some more action thing
It works! So you can update a flag on your database using try catch if you are checking if view has been rendered.
If something is getting executed, you might want to post your codes?