Page, View, Controllers – where to put my code?
Hi. Few people have had difficulties understanding which approach is better to choose, since so many things can be done in different ways. This post will clarify you regardless where you should put your code.
Quick-project (goal = develop fast)
For a quick project, we would focus on re-using as many components as possible. We also probably want to go with default templates.
If project is small in size, then it’s better if you put all your code into the API class. You can add function such as page_index($p), page_order_add($p), etc.
Inside those functions you should be manipulating standard Form, Grid and other view classes. Use setSource() to point to a right table in the database.
If you run into a form which needs to appear on multiple pages, then create lib/Form/OrderForm.php and then use this class instead of add(). When you redefine Form class use submitted() method instead of isSubmitted(). That would allow you to do all the checks and saves but still inside page you can use isSubmitted() (which calls submitted()), and perform some trickery.
Finally if you start to feel that some pages needs to inherit other pages, then it’s time to create class such as Page/Preview.php, and then create page/user/preview.php which will inherit Page_Preview and set necessary property. You can also set $api->page_class if you want pages to use certain class by default.
If you follow this pattern, that your initially small and simple project can gradually grow and is not going to require you to refactor.
Large projects (goal = flexibility and control)
As your project grows, you might start having difficulties with this approach. Especially with data management. So you should take advantage of some of the advanced features.
Models and Controllers
If you are interested in current implementation of MVC – please email me.
Model creates the role of basic entities in your system. You will be able to define for each model, which field it has and how it relates to other models. Model takes care of field types, validation. It also supports calculated fields – which are implemented as sql expressions or even sub-selects.
Models also allow you to implement methods. Those methods are very suitable to put business logic into it. The basic rule of a method – if method fails, then whole operation should fail. For example, you might have $subscription->extend(), which would involve numerous actions. If one of those fails – others have to be reverted (rolled back).
In contrast – on the pages if something fails, you may choose to continue with other visual elements.
So if you are interested in using MVC, please email me.
Agile Toolkit provides you with univ() chain, which you can extend easily. look into atk4_univ(), create copy, clear funcitons out and import your own functions. However in larger projects you might need to group functionality. It’s recommended that you create jQuery UI widgets and include them with $this->js()->_load() when needed. While univ() usually is being loaded in API::init(), a specific functionality / widgets can be loaded directly from page::init()
With addition of sub-pages, you can now create your lib/Page/Entity.php – which will combine functionality of editing, adding and deleting. You can rely on DSQL or Models to define behavior of particular entity, when you create page/entities/countries.php for instance.
API::addLayout() allows you to add dynamic functionality on global template. This could be a global log-in form for instance,which appears on all pages. It also can be menu or filter. Page content itself is implemented as layout_Content();
Multiple shared templates
Sometimes in larger projects you will need to switch the big template. For example you might add document printing. You don’t need menu for that. Approach of Agile Toolkit is that you define this in API::defaultTemplate(). You can look at $this->page or certain GET arguments to decide which template to use. Note that layout_Menu and other layout elements will look into your template and will not be initialized unless you have spot for them. So if your alternative template does not have <?Menu?> then it won’t be inserted.
Object add-ons (in future – controllers)
This is quite a powerful technique. It allows you to add additional functionality to the object by associating other class with it. Currently good example would be grid paginator and quick search. You use stock grid, and do $grid->addPaginator(). It includes additional object which does something extra.
Similarly you can write your own classes you might add into things. Good example is View_Hint(). Then add function into your form $form->addHint() which will properly work this out with the templates.
Certainly learn how to use hooks, especially if you are planning to create add-ons.
What goes where?
You put the stuff which will be needed on all pages in here, inside init. You also can add pages through functions, but it’s for smaller projects – you might want to keep pages consistently in the files for larger ones.
you can also add generic business-related methods here if you are not using models, such as sendClientInvoice(). Although highly recommended to clean those out, once your project grows bigger and either place them into respective models or use object add-ons. A good add-on sample is logger. So you might create class “Calculator” and then you reference it thoguh $this->api->calc->doBizLogic().
Generally – try to keep API for under 1k lines of code.
Each addon would primarily focus on a single and very narrow functionality. For example DB is added as an add-on. In Agile Toolkit 4 our goal is to completely clean out APIs and move all functionality into addons/controllers.
Try not to make very wide-ranged addons, it have to be light and do what’s necessary. Try to keep code betwen 50 and 500 lines. Create multiple add-ons if necessary.
If you look at other MVC implementation, you often need model, view and controller bundled together. You would have choice to inherit them from whatever you want, but very often you don’t need to.
Concept of pages in Agile Toolkit 4 serves multiple roles:
- Routing. Saying which browser requests lang on which code, and security – do not allow access classes which are not meant to be accessed.
- Configuration. Page determines which controllers, models, views and templates you are going to use and how.
This is really powerful combination. You can use pages without controllers, views or templates, or even you can use templates without pages. Here are some usage examples:
- Terms and Conditions: you don’t create page at all, but instead your API will look for template in page/terms.html and will wrap it up with default page class.
- Statement: on that page you would want to use a view (Grid) to display table. You don’t need to redefine default template, also grid can work with default template. You can use grid either with static data, setSource / DQ query or model (if you are using MVCGrid)
- Welcome page: on this page you would have multiple objects, but they also need to be arranged in a fancy way. So you will want to have page template and insert objects into specific tags. You might want for your views to re-use part of the page template, so something like this would work: $this->add(‘CompleteLister’,null,’news_box’,'news_box’)->setController(‘Controller_News’); You can have similar effect with setSource().
- Sub-pages. Let’s say you want to have page which allows to list, edit, add and delete countries to the database. (Refrer to : http://blog.atk4.com/multi-page-pages-finally/) You would generally create generic abstract lib/Page/Entity.php which you will then inherit and configure to use specific data-tables or models.
So – the bottom line – Page is for “binding” and “configuration”. Do not get used to have a lot of business logic on the pages. Also pages do a lot of JS bindings and UI behavior. But clean up business logic from the pages as soon as you can.
Keep your page under 200 lines of code. Init methods can go quite large, but make sure they are very simple to read. We tend to use short variable names on the pages and code is often being copied from one page to another instead of inheriting. That’s because client will ask to tweak certain page and other pages must not be affected.
Layouts are defined as functions inside API. You will have to also add them inside initLayout() by addLayout. Do not do complicated logic here, simply add some views and be sure to use proper template tag.
Keep those functions under 20 lines of code. Put everything else into view.
Here you keep model-related business logic. Depending on complexity certain models can grow up to 2k of code. You may want to work really hard on good inheritance. Also you should properly document all the external methods and make properties private where necessary. If you have to assign multiple developers, then put senior developers to work on the models, especially Java skills are beneficial here.
Models must not interact with Views in any way. They work only with data. They also do not have templates and should certainly do not output any HTML or JS.
In Agile Toolkit 3 controllers are between Models and Views. Controllers can access $this->owner, and somehow affect the way how model is displayed. This is good to make certain modifications to all form using your Model/Controller combination.
Controllers in Agile Toolkit 3 also have default Model with the same name and when you should always add Controller and not model directly. This would most probably change in Agile Toolkit 4 thought.
Controllers are usually small in size, up to 20 lines of code.
View is generic in a way because it does not know anything about field names or table names. If you are making your own views, make sure they are not tied in to particular database. Views would usually have defaultTemplate(), but from inside page developer may choose to use different template.
View can use multiple templates or clone templates or even use other views. View also can use JS, although it’s recommended to have specific jQuery UI widget for your view, if it becomes JS-heavy.
View size may vary between 10 and 200 lines. If your view grows past that – you might need to split it into multiple views.