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
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 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.
So, I am fairly new to Zend 2 and MVC as a whole, and I find myself in a situation where I want to follow best practices to make my code reusable and easy to understand.
The particular scenario I want to deal with is the following. Let's say I am writing an editorial application, where users can send articles, but they need to be approved before they are published. When you access an article /article/view/101 , you get a page with the article info on one side (Status, Author, Date, Title, Body) and on the sidebar you get a set of actions.
The set of actions (links) changes based on the type of user viewing the article (guest, user, reviewer or admin) and also based on the status of the article (draft,finished,published)
So the question is: Where do on the MVC model do I put the business logic to decide which actions (links) to put on the sidebar?
The controller does not seem appropriate because I would be adding Business Logic there, and also adding HTML (bad + bad)
The view does not work either because I would be adding business logic.
A service doesn't seem to work either because it seems I would be either adding HTML, or calling partials from there, and that should not be done either...
The only thing I could think of is doing the Business Logic in a service or helper (since more than one model is needed, article and user) and return an 'array' of actions (no HTML). Then the view processes those to actually get the HTML, but I'm not sure if that is the way to do it, and wanted some experienced input.
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'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 }