I have few instances of Zend_Acl objects, like this one (one for each module):
class My_Acl_Module1 extends My_Base_Acl
{
public function __construct()
{
parent::__construct();
$this->addResource('News_Model_Entry');
$this->deny('guest', 'News_Model_Entry', 'index', new News_Model_Acl_Assert_CategoryPrivate());
}
}
class My_Base_Acl extends Zend_Acl
{
public function __construct()
{
$this->addRole('guest');
}
}
How to merge them into one ACL object to use in navigation container?
Edit
Some more info:
I don't use controller resources in favor of model based resources (models, forms implement Zend_Acl_Resource_Interface). All of them have method isAllowed()
I use modular directory and reusable modules (separate models, configs, routes etc.)
My application knows all installed modules, controllers, action (structure already parsed, not in real time)
So I'm looking the way to follow this scheme, and separate the ACL for each module. I don't want to use application resource, because it is a waste - acl is not needed always.
I have an action helper which instantiates module specific ACL only when it is really needed. But sometimes I'd like to have global application ACL available too (eg. when I'd like to pass it to the navigation view helper or controller plugin).
My module ACL classes have all just one method: init().
Dirty solution I see, is to parse the source classes and merge the files into one method for new class.
Any suggestions?
I think this is almost impossible without the application knowing a bit more about itself. I had a similiar problem a while ago too and didn't find any satisfying solution. When merging you'll loose some information (the module) which you need later on in your navigation.
The first solution I had back there, was to iterate over all acl-files in each module and create a custom ACL-merge function to merge them all. This actually did work but I didn't like the idea of a file-scan over my complete application (even if the results were cached).
My last approach was to add more data to my application: For every linkable action I defined the corresponding acl-file. This wasn't as much work as it may sound. Mainly because my controllers/models do match almost exactly. So I have a model 'News' and a controller 'News' which handles the access to it and maps every action of the model to the frontend. So I only had to specify the acl/controller relations. (I used a custom element in each navigation container to save this).
Related
I just started taking a look at the MVC pattern.
My question is:
Where would I put my other class files (Database, User, Logger, Mailer, etc)? Should I create a new directory for them, e.g. libs?
Should I instantiate the classes in the Controller inside the model function?
<?php
class Controller {
protected function model($model) {
require_once('../app/models/'. $model .'.php');
return new $model();
}
protected function view($view, $data = []) {
require_once '../app/views/'. $view .'.php';
}
}
Where would I put my other class files (Database, User, Logger, Mailer, etc)? Should I create a new directory for them, e.g. libs?
Putting them in separate files since they all provide different functionality should be fine. There is no difference in your directory naming - as long as it matches the naming conventions of your project or general (probably, even better).
Should I instantiate the classes in the Controller inside the model function?
No. As far as I see it, the flow could be similar to:
The index file receives the request and initiates a new bootstrap instance
bootstrap sets the throwable handler and the router
router then calls the respective method basing on the request method and uri provided by matching against a set of routes
The matching route initializes all the components of the MVC triad and the callable method. The components (Model layer, View layer, and Controller layer) are passed to the method called by the router. In my case, I call the class FrontController, the method init.
Basically, the init is the place where the MVC triad is actually made. Model layer is responsible for business logic, persistence, etc. It is important to note that Model is not a single file or class (the same for View and Controller). Both View layer and Controller layer consult the Model layer to do the respective actions. View layer's mission is to manage the output, for example, decide wether the output will have Content-Type of application/json or text/plain, or which Template to render. Also, Views are not Templates, which are meant for displaying the data. Note here, Views ask the necessary data directly from the Model layer; there is no interaction with the Controller layer whatsoever. Finally, Controller layers steps in when there is a need for interaction, for example, a user submits a form, the respective Controller filter the input and calls the method from the Model layer.
As MVC has three major parts, I'd recommend (and noticed that almost all frameworks on the market do so) to create a directory of each of the three components, and place the classes in the appropriate directory.
As regarding the other components, Database is a utility and can be placed for example in a lib directory, User is a model and can go to the model folder, and Logger/Mailer can also go to the lib folder. These are examples and not something to strictly follow.
As for instantiation, each Controller can define the list of models and libraries it depends on and have the MVC framework handle the initialisation of those objects. You'd follow the dependency injection pattern this way.
I currently have a Helper (FieldFormatterHelper) I created to assist with consistent formatting of certain types of data (dates, booleans, enumerated types, etc) across various views.
In particular I store certain enumerated types in MySQL using an integer and then display a corresponding string value in my views. The problem I'm running into now is that while this Helper works well for my views for things like "view" and "index" Views that administrative users access from the site's backend, I also have afterSave() methods in several models that create JSON outputs for Mustache rendering on the frontend.
I understand, more or less, the core ideas of MVC and the basic arguments for, say, not trying to access a View Helper from within a Model, but I need some sort of universal access to the functionality my Helper provides. Certainly I could define my enumerated type mappings in my frontend JavaScript and other places I might need it, but that violates the DRY principle and would ultimately be a pain to maintain.
So, the question is: If it's not appropriate to access/load my Helper from within my Model, where, and how, should I encapsulate this functionality so both my Models and various Views can utilize it?
Ideally, I would be able to continue using my existing FieldFormatterHelper--perhaps modifying to be a wrapper for this new container.
Any guidance on the ways of CakePHP and MVC in general would be appreciated. Thanks.
You can implement it as a static method on a class and use it anywhere
class Utils {
static public function formatField($args) {
// your implementation
}
}
then just have your helper defer to the static method call using
Utils::formatField($args);
You can create a static function and put it in app\view\helper\AppHelper.php. Then when you want to call it you put
App::uses('AppHelper','View/Helper');
at the top of the file and call it like this:
AppHelper::staticFunction()
I'm trying to think of a way to create a plugin architecture for my own framework. I've read numerous topics and post here and on other sites. And basically i've came to the following solution which seems to be the only good option in PHP (currently).
The idea is that every class extends a sort of observer like class. So a Template class, BaseController etc. always extend a Plugin class.
class BaseController extends Plugin
{
public function __construct()
{
// Plugin check, notify all loaded plugins
$this->checkForEarlyHooks();
// Init some standard stuff
$this->view = new Template();
$this->baseLayout = 'layout.html';
$this->something = new Something();
// Plugin check, notify all loaded plugins
$this->checkForLateHooks();
}
}
So what basically happends here is that when an indexController extends a baseController a plugin check is done. In this case for the constructor. This can be convenient when you want to do a some sort of login check with a plugin, before an Action method is actually invoked.
The Plugin class can resolve from what class is got called from and knows for what functions to look for in the loaded plugins.
Also note that it checks the loaded plugin list 2 times. One before anything is loaded (early) in the constructor, and one when all vars are loaded (late).
I can also add variables to the "checkForLateHooks()" function. So the hook functions can manipulate those too, like the 'baseLayout' variable.
A hook function would look like this:
public function hookConstruct ( &$baseLayout )
{
$baseLayout = 'login.html';
}
Now basically my question is, is this approach any good? I know there are probably alot of other ways to do it too. But i mainly don't want to run into design problems later on. It seems like a good idea now, but you never know how things work out later on...
If i remember it right (from all the posts the i've read), this is kinda like WordPress does it (and some other frameworks).
UPDATE: answer now reflects the up-to-date links and better description.
There are certainly many different ways to design a plugin system and perhaps asking on https://softwareengineering.stackexchange.com/ would give you more ideas, but I'll try to help by sharing my ideas and experience.
I'll share some of my own experiences which I've learned through a series of my own frameworks. Currently Agile UI and Agile Data both support many wasy to be extended, but I'll focus on the "Components"
HOOKS
When you look to inject code into the existing object, a hook is a standard way to go. It's the best option for extending the application or flow with an established structure.
While refactoring my frameworks, I've separated hook implementation into a separate trait and documented it here: http://agile-core.readthedocs.io/en/develop/hook.html
Host application:
... some code here ..
$this->hook('beforeInit');
$this->init();
$this->hook('afterInit');
... code goes on ..
Plugin:
$host_app->addHook('beforeInit', function($object) {
echo "About to execute init of $object";
});
UI COMPONENTS
Components present a different design pattern, which is suitable for the User Interfaces. You start with the page/app layout which is then broken down into Menu, Header, Footer, Content.
A component is an object which can be associated with Layout or the other component. Each component is capable of rendering and passing extra HTML/JS to its parent. Most of components would also be an interractive objects.
This approach is called "Render Tree" and application execution goes through 2 stages - "initialization of a render tree" and then "rendering".
Host Application:
$layout->menu = new \atk4\ui\Menu();
$layout->add($layout->menu, 'TopMenu');
The above code show how a new Component (Menu) can be initialized and inserted into the $layou. Furthermore the HTML output of $menu is directed into {$TopMenu} tag, which is defined in the HTML template of Layout.
Plug-in can interact with the render tree by:
adding more components anywhere in the tree
affecting existing components (e.g. add new menu item)
destroying any of the existing components
When those approaches are combined, you can use something like this:
$app->addHook('afterInitLayout', function($app) {
$app->layout->menu->destroy(); // remove default menu
$app->layout->menu = new \plugin\SuperMenu();
$app->layout->add($app->layout->menu);
});
This can be used to replace standard menu with a more powerful implementation from your add-on.
My implementation of components is documented here:
http://agile-ui.readthedocs.io/en/latest/view.html#initializing-render-tree
UI / DATA SEPARATION
Although perhaps not so much answer for a question, but another powerful way to extending is a separation of concerns. All of the UI components in Agile UI do not know how to do anything with data.
While many UI generators require developer to manually build them up and link up with data, I am injecting "Model" object like this:
$form->setModel(new User($db)); // populates name, surname and gender fields
Demo: http://ui.agiletoolkit.org/demos/form2.php (2nd form)
In this approach object User contains enough meta-data for the Form to populate it's fields, captions perform validation and also save/load data.
Since "User" class can also be declared in an add-on it makes a pretty powerful way to extend existing functionality by adding support for new data entities.
OTHER APPROACHES
Some other approaches to extend with add-ons include:
Factory:
The ability to resolve class specified as a string into namespace / file:
$api->add('MyClass');
Gives ability for add-on to re-route standard classes but is not very friendly with type-hinting in IDEs.
New types / Traits:
Add-on can provide new classes adding Persistences, Table Columns, Form Fields, Actions etc.
CONCLUSION
I think add-on deign boils down to:
simplicity of installation and use
does add-on need to operate out-of-the-box?
avoiding conflicts between add-ons
defining different add-on types - authentication, UI, behaviour
can add-on extend another add-on
will add-on fit application design, data and architecture?
give a lot of power to add-ons without sacrificing performance
Have a look at PHPPlugin on https://github.com/gheevel/PHPPlugin. A simple PHP plugin framework inspired by eclipse and jira plugin architectures. Basically, your application can define extension points and plugin instances can register on those extension points for providing extra functionality. Works well in combination with composer and/or symfony.
I do have an module e.g. account. Of course you will find a file called in acount/actions/action.class.php.
The PHP-file action.class.php is getting big. Is it possible to extend it?
for an example:
/account/action/action.class.php
/account/action/action2.class.php
If it is possible, how does the code look like in action.class.php and in action2.class.php? And/or where should I enter something in a config-ymal-file?
Thanks in advance!
Craphunter
Symfony actions can be declare in two flavors:
A big actios.class.php file that inherit from sfActions
Multiple xxxAction.class.php file that inherit from sfAction
The are both basically the same but these cant be mixed (you must decide if you want 123123 files, 1 per action, or one big fat file).
Here its the symfony reference on this matters: Symfony Front Controller - Actions check the Action section.
It sounds like the action class is getting too big because you have too much stuff in it. I would suggest breaking it into multiple modules or moving relevant parts of the logic code into your models.
Adding an include for an action2 file is not how Symfony 1.4 is intended to be used and will likely just lead to all kinds of other headache.
PHP doesn't support 'partial classes' like C#. So you have two options:
Create an inheritance chain, by creating a baseActions class which inherits from sfAction. Then create your MainActions class which inherits from baseActions. You can add different layers of inheritance.
Group functions in seperate classes. Override the execute() function in your actions file, and manually call the function on the class you need.
Also consider if some of the behavior you're implementing in the action (controller) belongs to the model (or the view) instead. If the really belongs to the action just follow guiman's advice and create an action per class inheriting sfAction.
If I have a CRUD module based on a model (as generated by the CLI), and I have to define additional behaviors for that model, I tend to create another module to contain that behaviours. E.g., considering a model Article. I could create an article module for the CRUD behaviors (generated), article_support for additional behaviors like moderation, activation, etc. and perhaps article_ajax for async requests.
I'm thinking of re-working my MVC before I get to far along with it. At the moment it uses a sinle controller which takes the user input and determines the model. The model has maby differ methods which I call actions, because one is called automatically. The model loads its view file, which again holds many different methods. The model can set properties which can be used in the view. Then the controller calls th template classwhich parses the output for display.
Is this the bst way to do it?
Or should each different part (news, contact, about) have its own controller, which loads a specific model and view. Essentially, instead of grouping methods into a single file, it uses multipe files.
I'm kind of lost as to how I should do it.
Cheers
Start using a MVC that works and is well-known like in Symfony or Cake. From that you will decide:
what do to in your own, knowing the best practices;
to drop your own if you feel like you can save time by using theses.
If you are thinking of advancing your own MVC model, like #e-satis have said, you will need to experience what is happening in already developed systems. However, as based on my experience in designing MVC model and determining what is there in opensource community, I stick back to my own MVC for two good reasons. One reason is the flexibility of customization and the other is own MVC privacy.
I used the following approach for MVC design pattern.
A Router.php file identifying user-request urls. This router will be able to fetch controllers and include the file and call the controller default method.
The loaded controller is also able to load other controllers if required to function. This is done using a global method, where all controller Class will extend to a MainController Class which is able to call to other controllers.
I do use a global registry to set and get variables from one controller to the other.
The Models are used to get the Data from Table, and most of my Models will represent Database functions which includes CRUD (Create Read Update Delete). So that a controller can easily manipulate database table data using a model.
Naming conventions in all controller, models, and views is also important, if you want to system to be more intelligent to identify the required action knowing the file name.
I use the Views separately for each type of controller. And these views will be sent to a Master Template View file.
Same as models, the controller will be able to set Views to Master View.
There are other customizations which you can do, like applying security methods before calling a class, or after calling a class/controller/model/view etc.
This is done by the MainController, which it will always look into a folder with autoload class which states what files should be loaded before and after of different actions during the process of building the content and delivering the output.
MVC is not a small scale idea, but it is a design idea which can always be developed. There are so many PHP MVC open source frameworks to be found if you know how to search the major search engines like google.com
But I do advice you that, MVC is not a good solution if you are simply developing a small dynamic website, as it will consume more time in developing compared to developing small websites. MVC is ideal, if you have business logic and needs system automation in order to avoid most routine tasks of developing, and like that I would say MVC is most ideal for larger applications.
The model loads its view file
The Controller should act as a Mediator between the Model and the View.
The Model shouldn't be aware of the way the view renders it, and also the View shouldn't be aware of any kind of logic of the Model.
In theory, if your MVC is well structured, you should be able to represent the same Model with different types of Views (HTML, XML, JSON for example).
Build FrontController which parses request uri and decides which controller to load and which method to run. With .htaccess rewrite all request to index.php
//index.php
class FrontController{
function run(){
//parse request uri here /comment/new
//load controller comment
//run controllers method new and pass some request attributes
}
}
// ../controllers/comment.php
class Controller_Comment extends Controller{
function new($request){
//do stuff here
}
}