(what they do and contain (business logic at different levels))
Before we talk about Actions and Models in Agavi let's take a brief look at the MVC (Model-View-Controller) paradigm. The idea behind MVC is that the view (a presentation layer, user interface) and the model (an object, or alike, that represents some information about your application domain) should be de-coupled in such a way that the model is totally independent of the view. In MVC this is achieved by putting a controller between these two (this is not entirely accurate and in any case a simplification but we don't go into that).
When looking at Agavi's classes it's easy to see what's the view part and where's the controller. Models are of course your own classes although they can derive from an AgaviModel class. But where do Actions stand in this scheme? Orthodoxly they are a piece of the controller (or, some could argue, the controller itself) but Agavi's Actions can be seen as the glue between the model and the controller. The Controller decides what Action to trigger and after that it's up to the Action to execute the proper domain logic. Some trivial domain logic can reside in the action but to keep your application in accordance with MVC the actual model part of your design should be separated from the action.
Understanding the idea and the value of MVC is essential in all software designing. For an in-depth rationalization of MVC paradigm you can consult Martin Fowler's Patterns of Enterprise Application Architecture (ISBN 0321127420).
As said before you write your own models for the application and Agavi doesn't really mind how you do it (some people like to make their models framework independent so that they can be used anywhere). There are, however, a few things to point out that can make it easier for you to use your models.
Firstly you can ask Agavi to handle the loading and the
initialization of a model. Agavi divides models into global models and
module models. Global models go into app/models/
and module models into
app/modules/MyModule/models/. An instance of a
model is retrieved using getModel(string modelName [,
string moduleName] [, array parameters]) of the
AgaviContext. The first, and the only required,
parameter of that method is the name of your model class. The second
parameter is the name of the module where the model resides. Set/leave
it to null to retrieve a global model. If given, the
parameter array is passed to the initialization method of you model or
to the constructor if initialize(array) is
not defined.
Secondly you can make singleton models simply by implementing
.
No other changes are needed to your model. Agavi stores the instances
of your singleton models. To retrieve an instance, simply call
AgaviISingletonModelgetModel() on the
AgaviContext.
Examples:
/* Loads /app/modules/Dogs/models/Dog[Model].class.php and initializes it with a name and a breed. */
$myDog = $this->getContext()->getModel('Dog', 'Dogs', array('name'=>'Nelli', 'breed'=>'Novascotian retriever'));
/* class SinglesBar implements AgaviISingletonModel */
$bar1 = $this->getContext()->getModel('SinglesBar'); //loads and initializes the model.
$bar2 = $this->getContext()->getModel('SinglesBar'); //returns the same instance of SinglesBar as the line above.(actions: execution, execution methods, serving request methods, default view name, validation, security)
Like we noticed before, actions can be seen as a part of the
controller, or as glue between the controller and the model. The
controller can execute actions in two ways depending on the request
method. In a web-context this could mean the controller would call the
executeRead -method for a HTTP GET-request or
executeWrite for a HTTP POST-request. Both
methods take an AgaviRequestDataHolder -object
as parameter. If the action contains an
execute()-method and neither
executeRead() or
executeWrite(), the controller will call the
execute() method regardless of the request
method. If the request method is POST but it does not implement the
write method or similarly the request method is GET but the action
does not implement the read method, it can run a default view by
implementing the getDefaultViewName
-method.
Before the controller executes the execute-method, it will check two things: does the action require security (ie a user that has logged in) and what credentials, if any, the user must have to be allowed to execute the action.
The former is simply done by adding an
isSecure() -method to the action which
returns true if security is required. Credential requirements are
defined by implementing a getCredentials()
-method which can return either a string indicating a simple
credential, or an array of credentials.
To require the credential "Edit", simply return "Edit" from
getCredentials(). To require both "Edit" and
"Write" -credential, return array("Edit", "Write"). To
require EITHER the "Edit" OR the "Write" -credentials, you would
return array(array("Edit", "Write")), and to require the
"Edit"-credential AND EITHER "Write" or "Foo" (or both), return
array("Edit", array("Write", "Foo")).
After the controller has decided if the user is allowed to
execute the action, validation occurs. If the validation fails, the
action's handleError() will be run, which
would, like the execute() return a string or an array indicating which
view or action/view -pair should be handed the control. The validation
error messages are available for the templates using
$container->getValidationManager()->getErrorMessages()
which returns an array of messages. For more information on action
validation, see section 11 in this manual.
When the action has finished, it must tell the controller which
view to execute. This of course depends on the outcome of the action:
if we are successful, we would want to pass the control over to a
SuccessView, if we had some form of error, we
would use ErrorView and if we need input from
the user, we'd use InputView. The action can
return a string like "Success" to indicate the action finished
successfully, and the controller would use the
ClassnameSuccessView, or it can return an array
with two indices specifying the action/view pair to hand the execution
to.