PcBaseArModel ¶
Base "Active Record" model that adds capabilities to Yii's CActiveRecord
Introduction & Features ¶
This class is typically used as a base class to all your AR (Active Record) classes in a Yii project. It adds some nice and useful features:
- Automatic timestamping when a record is being created and updated.
- "Safe" updating and deleting of record using "Optimistic Locking".
- Also method for safe updating using given number of retries in case of failures.
- Convenient method for record attribute trimming for breadcrumbs. For example, 'title' attribute that needs to be trimmed since it can be long (common requirement since breadcrumbs have limited screen real-estate).
- Can tell if an object 'is dirty'.
Requirements ¶
- Tested with Yii v1.1.9 and v1.1.10.
Usage ¶
Installation ¶
Extract the contents of this package. Place the extracted PcBaseArModel class inside /protected/components.
DB changes required from extending classes ¶
- Models that extend this class should have the following fields defined in their DB schema (MySQL syntax given):
created_on
timestamp NOT NULL DEFAULT CURRENT_TIMESTAMPupdated_on
timestamp NULL DEFAULT NULLlock_version
int(11) DEFAULT '0'
- An example to a custom AR class that uses this class:
Sample code ¶
Should be as trivial as:
class MyClass extends PcBaseArModel {
//...
}
After the above is done you cna use this class API as briefly described below.
API ¶
After you have a model class that extends this class you can use its methods. Please refer to the code for complete documentation. Here's a list of public attributes and methods (and constants) to be aware of:
Constants ¶
- LOCKING_ATTRIBUTE: DB column name for holding the 'lock version' of the record (used for optimistic locking).
- CREATED_ON_ATTRIBUTE: DB column name for holding the 'created on' timestamp of the record.
- UPDATED_ON_ATTRIBUTE: DB column name for holding the 'updated on' timestamp of the record.
Attributes ¶
- breadcrumbsStringLength: trimStringForBreadcrumbs($str) uses this parameter to trim the passed $str to this length.
Methods ¶
- safelyUpdateByPk(): Safely updates a record in optimistic lock mode. See code for full parameters list. This method is based on CActiveRecord.updateByPk() and uses a similar as possible parameters list.
- safelyDeleteByPk(): Safely deletes a record in optimistic lock mode. See code for full parameters list. This method is based on CActiveRecord.deleteByPk() and uses a similar as possible parameters list.
- safelyUpdateByPkWithRetry(): Similarly to safelyUpdateByPk() above, this method does the same but it will retry the update if the last one failed. Both the number of retries and the interval to wait in between attempts are configurable.
- isDirty(): Would tell you if the object you're playing has changed since it was loaded from the DB or not.
- isAttributeDirty() - A method that can be used to check if a specific attribute has changed in the current request lifecycle, after initial loading from the DB (this is the basis for the comparison). Useful in variety of situations like conditional validation, which is what I needed it for.
- trimStringForBreadcrumbs(): Trims given strings according to length stated by $this->breadcrumbsStringLength and append ".." if was indeed trimmed.
- checkExists(): Given a pk value, tells if a model record exists in the DB or not. This is a simple wrapper to CActiveRecord.exists() method. If your extending class primary key column name differs from 'id', you'll need to override also primaryKey() aux method.
Exceptions thrown ¶
The optimistic locking methods safelyUpdateByPk() and safelyDeleteByPk() can cause two types of exceptions to be throws. Your code should catch those exceptions and handle them according to your biz logic. The exceptions and their meanings are given below:
- PcStaleObjectErrorException. This exception is thrown if the object to be saved is determined to be 'old' when actual saving occurrs. This is a common way to implement the "safe" in the method's name - if its thrown it means that the object to be updated, from the moment the update request has been issued until the actual DB updating has been attempted - was already updated by someone else and if it was updaetd nevertheless - we would have overwritten the data just saved by someone else.
- PcBaseArModelUnsupportedConditionException. Both of the 'safely..*..ByPk()' methods internally use CActiveRecord updateByPk() and deleteByPk() methods. As such, both accept as similar as possible parameter list. One of those parameters is 'condition'. This class supports only string based 'condition' (as opposed to updateByPk() for example that receives a 'mixed' type condition parameter. See more here). This exception is thrown in the event of an unsupported 'condition' passed to the relevant methods, as just described.
Resources ¶
Change Log ¶
- 30 Aug 2012: v1.3 : added isAttributeDirty() - a method that can be used to check if a specific attribute has changed in the current request lifecycle, after initial loading from the DB.
- 7 Aug 2012: v1.2 : added checkExists() - a handful static method to check if a model record with a PK exists or not.
- 1 Aug 2012: v1.1 : Bugfix - forgot just one word on the class definition - "abstract"... . Shame on me :-) . Fixed.
- 5 July 2012: v1.0 : Important bug fix and more. IMPORTANT: this release is NOT backward compatible. But the good news is that it requires only an implementation of a simple method in child classes. See full change log here (first sentence was dropped and its "_fixes #1 (multiple calls to safely*() trashed the condition on the lockversion)").
CTimestampBehavior
you can use
http://www.yiiframework.com/doc/api/1.1/CTimestampBehavior
A Couple of Suggestions, and a Question Regarding Giix
Whenever an extension like this is published, you always see someone saying, "Just use CTimestampBehavior ..." For the most part, CTimestampBehavior is so narrowly focused, does so little, that it isn't a real fix for much. Fortunately, extensions like this do a lot more than CTimestampBehavior. One thing that I did not see in this extension is created_by/modified_by. A lot of us who want to track when a model was created/modified also want to track who did the creation/modification. It would be great if you included that.
More importantly, like so very many folks, I use Giix for model/CRUD generation. Using it, I end up with MyModel extends GxActiveRecord, not CActiveRecord. (Actually, GxActiveRecord extends CActiveRecord.) Will your extension work if I use it to extend GxActiveRecord? If not, I would suggest that you include that as an option.
Another thing I would suggest is to allow the developer to specify the name of the timestamp fields. I for instance have used created instead of created_on and modified instead of updated_on. It would be a lot of hassle to go back through each table and model class to make those changes for no real benefit other than to implement your extension. Allowing user-specified field names would greatly enhance the usability of your extension.
@LarryTX
Thanks for the feedback.
On the issue of using a single, big super class that has lots of functionality and which everybody extend vs. composing child classes directly from smaller components - there is not conclusion in any direction in my opinion, despite that PcBaseArModel is heavily leaning toward the former. Adding functionality by means of attaching (behaviors, filters...) components to other components is very comfortable, flexible and has other benefits. OTOH, it raises the complexity to some degree by adding potentially many more classes and their 'binding mechanisms'. Still, I wouldn't avoid any.
I'm not familiar with GxActiveRecord. Sorry - please try and see. I assume, but this is not based on any real examination, that with little work you could have either the child of the other.
Regarding custom attributes for created on/updated on, you edit the PcBaseArModel constants of CREATED_ON_ATTRIBUTE and UPDATED_ON_ATTRIBUTE. Please refer to the source code. I usually document heavily inline as well.
Good luck with your project!
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.