
Continuation of the
previous postThe area of responsibility of the model and its place in the project
Models are intended only for handling data (search, saving, deleting), as well as related static files, as they are also an integral part of the data structure (photo, video). Naturally, when deleting a record, the media files associated with it should be deleted, for this it is convenient to use beforeDelete () or afterDelete ().
Simple business logic can be implemented in the model, for complex - it is better to use the service layer (Service Layer) so you save the model from the mass of dependencies and will not inflate the divine object from it.
In models should not appear:
- HTML, CSS, javascript
- Superglobal arrays ($ _GET, ...)
- Global variables
- Verification of access rights (this moment may be controversial, practice shows that access control to operations should be carried out in the controller)
- Text strings not wrapped in Yii :: t ()
Models should have as few dependencies on other components as possible, so we can make it portable and use it in other projects. In this case, the components on which the model depends should be installed by setters (for example, User :: setEmailComponent ('email')). You can also assign components in User :: init (), but you must also check for its presence. If the component is secondary and unavailable, the model must work without noise, however if the component is paramount, then an exception should be thrown at the initialization stage.
These simple rules will allow your code to be supported by a team of programmers, and it is sufficiently flexible to extend the functionality.
Status check
Usually, checks are done in different places, both in controllers and in views, and checking often looks like this:
')
However, if you create an isActive () method that does this check, the code will become more controllable, especially in the context of frequently changing TK. Also sometimes it will be useful to create a method that describes checking for the possibility of transition to a different status status. For example: if a record has the status - “Spam”, then it cannot be transferred to the “Active” status, but it can be transferred to the “To check” status. It is desirable to place all this logic also in models. (This can be useful when building workflow systems)
Use flag fields
To save the size of the table (the number of columns) and to have a more flexible model expansion functionality, use fields with bit flags.
Such fields are usually represented in the database by the type integer (but not necessarily) and are able to place a fairly large number of flags. I already touched flag fields in the previous article. Now I will show practical application. (Bit masks are described
in detail
in this article ).
Example: There is a user model in which several simultaneous flags can be assigned to a user - “best author”, “phone confirmed”, “email confirmed”, “left a request for moderator post”
Fragment of the model public $flags = 0; const FLAG_CONFIRM_EMAIL = 1;
In the MySQL query, the check will look like:
SELECT * FROM `User` WHERE STATUS & 6
In the example, a
named group of conditions is created; this allows searching as shown below:
User::model()->withFlags(User::FLAG_CONFIRM_EMAIL)->findAll();
Relational relations
Relational relationships are well described in the
official documentation .
In practice, there are such nuances.
- If the relational relationship is built correctly using FK, then everything should work clearly, since the integrity of the data is provided by the database.
- If the relational relationship is built only by means of YII (and it is not possible to use FK), in this case you should not use direct access to the variable designating the connection, instead use CActiveRecord :: getRelated () .
- Checking the correctness of connections (correct ID) must be controlled during validation and in the beforeSave () handler.
- Deleting related data must be implemented in the beforeDelete () handler.
Representations for attributes
HTML code is sometimes found in model code - it does not belong there. If there is a need to display an HTML-formatted attribute for this, use either the widget or create a helper for the model (a class with static methods). examples are shown below.
Helper example class UserHelper{ public static function getProfileLink(User $model, $htmlOptions = array()) { return CHtml::link($model->getUserName(),$model->getProfileUrl(),$htmlOptions); } }
Widget example class UserAvatar extends CWidget { public $emptyPhotoUrl = "/static/images/no-photo.png"; public $model; public $htmlOptions; public function run(){ $avatar = $this->emptyPhotoUrl; if($this->model->hasAvatar()) { $avatar = $this->model->getAvatarUrl(); } echo CHtml::image($avatar,$this->model->getUserName(),$this->htmlOptions); } }
The widget and helper are conveniently used in views, the helper is convenient when used inside the widget CGridView.
Model journaling
For debugging models, especially for catching a difficult-to-reproduce error, it is sometimes useful to be able to learn the chronology of events.
To do this, you can add logging methods to the model, and keep a log (log file) for all operations performed with data. The implementation of this simple mechanics can be any, but it is useful to recall the CLogRoute component and configure one of the routes for your model, the log can be kept either in the database (caution, decreases performance) or in simple files. It is also useful to write to the log user data on behalf of which the operation is performed, it will simplify the analysis of flights.