Best practise when working with models?

Example: I have a User model. Each user can block another user. This should be done internally with help of an additional UserBlock model. If a user (victim) blocks another user (attacker), the attacker shouldn’t be able to see for example the online status of the victim user. Also the attacker can’t send private messages to the victim and so on…

Now see my approach below and let me know what you think. Maybe there is a better solution? Note that I use DAO instead of actual ActiveRecord (for performance reasons).

User model:




public static function isUserOnline($requestUserId, $targetUserId)

{

		

   if (0 !== $requestUserId)

   {

      if (true === UserBlock::isUserBlocked($requestUserId, $targetUserId))

      {

         return false;

      }

   }

		

   // Other code to check if user is online...

		

}



UserBlock model:




public static function _add($requestUserId, $targetUserId)

{

		

   $dbCommand = Yii::app()->db->createCommand("

      INSERT INTO `UserBlock` (`requestUserId`, `targetUserId`)

         VALUES ({$requestUserId}, {$targetUserId})

   ")->execute();

		

   Yii::app()->modelCache->delete("UserBlock.isUserBlocked.{$requestUserId}.{$targetUserId}");

		

}

	

public static function _remove($requestUserId, $targetUserId)

{


   $dbCommand = Yii::app()->db->createCommand("

      DELETE FROM `UserBlock`

         WHERE `requestUserId` = {$requestUserId} AND `targetUserId` = {$targetUserId} LIMIT 1

   ")->execute();

		

   Yii::app()->modelCache->delete("UserBlock.isUserBlocked.{$requestUserId}.{$targetUserId}");

		

}

	

public static function isUserBlocked($requestUserId, $targetUserId)

{


   if (false !== ($blocked = Yii::app()->modelCache->get("UserBlock.isUserBlocked.{$requestUserId}.{$targetUserId}")))

   {

      return (bool)$blocked;

   }


   $dbCommand = Yii::app()->db->createCommand("

      SELECT COUNT(*) FROM `UserBlock`

         WHERE `requestUserId` = {$requestUserId} AND `targetUserId` = {$targetUserId}"

   );

		

   $blocked = $dbCommand->queryScalar();

		

   Yii::app()->modelCache->set("UserBlock.isUserBlocked.{$requestUserId}.{$targetUserId}", $blocked);

		

   return (bool)$blocked;

		

}



So when displaying for example a list of members I can do this while in a loop:




if (true === User::isUserOnline(Yii::app()->user->id, $currentUserRecord->id))

{

   echo "online";

}

else

{

   echo "offline";

}



What you think about this? From performance point of view it should be very fast since caching is used. But from logical point of view I’m not sure. Maybe I should use a solution with relations or something? Any comment appreciated.

I like your approach, even though i couldnt understand :)

Since there is so little information and example about DAO using, i m not sure how to use.

For example do we have to use static functions when using dao, or should we extend our model class from any base class like CModel when using dao instead of Active Record.

And also i couldnt found any information about the modelCache class. When & how sholud we use modelCache class?

Thanks!

You can use DAO wherever you want. You don’t need to extend from CModel. DAO is basically like a wrapper for the mysql_ php commands. Also note the functions I posted are custom ones were I choosed to use DAO for performance reasons. I could have put the functions in another class like “UserUtils” (which doesn’t extend from CModel) - this has nothing to say. These are just custom functions related to a model. See here for more info about DAO.

I have defined "modelCache" in config as cache component. This is only for better seperation so I can later simply change the cache backend (file, xache, memcache and so on). There is no model-cache in Yii, but your are free to define a cache component and use that to cache model data like I did here.

I think that your add command, instead of just deleting from cache, should delete/add (or just update) the cache value.

Additionally, instead of storing a 1-to-1 cache, why not store all blocked ids in cache for the "victim"?

Then you can just search the “victim’s” cached array to see if the current user (“attacker”) exists in the array.

If you take that approach, you also need to update your delete command to just delete the single id from the cached array.

This would work fine as long as you don’t have a memory limitation (which most servers don’t nowadays), and IDs are very small in size.

Otherwise, by doing individual queries/caches, you’ll essentially be reproducing a possible performance hit, such as what AR would introduce.

As far as doing something with relations, I don’t think that extra step is necessarily needed.


Furthermore, if you go with the approach I suggest above, you could even update the cache for both “victim” and “attacker” upon add/delete, so essentially both should already have an up-to-date cached array of “people I’m blocking” and “people that have blocked me”. This might be needless though :wink:

Thank you intel352, I guess it’s indeed a better solution to cache 1:many instead of 1:1.

Well I’m basically still in planing phase, but I want to create big community project. If someone has useful informations related to this (eg db and caching aspect), please let me know.