[PHP] Tag projection and unit of work

Source: Internet
Author: User
[PHP] Tag mappings and unit of work
Tag Mappings

There may be two identical values in the system, but not the same referenced object, so that duplicate objects may be read from the database, which creates unnecessary queries.

tag mapping is a class objectwatcher that manages the realm objects in a process to ensure that duplicate objects do not appear in the process.

Tag mappings prevent re-reading of database query data and query the database only if there is no object in the Objectwatcher class that corresponds to the tag mapping. This ensures that in a process, a single piece of data corresponds to only one object.

The code is easy to understand and is an operation that accesses array values.

Objectwatcher Code:

namespace Demo\domain;use \demo\domain\domainobject;/** * Tag map */class objectwatcher {private static $instance;//Tag Map PR Ivate $all = Array ();p rivate function __construct () {}public static function getinstance () {if (!isset (self:: $instance)) {self:: $instance = new self ();} Return self:: $instance;} /** * Gets the key value corresponding to the object * @param domainobject $obj */public function Getgobalkey (DomainObject $obj) {$key = Get_class ($obj). '_' . $obj->getid (); return $key;} /** * Added to all * @param domainobject $obj */public static function Add (DomainObject $obj) {$instance = Self::getinstance (); $ Key = $instance->getgobalkey ($obj); $instance->all[$key] = $obj;} /** * Delete from all * @param domainobject $obj */public static function Delete (DomainObject $obj) {$instance = Self::getinstance (); $key = $instance->getgobalkey ($obj); unset ($instance->all[$key]);} /** * Determine if the tag exists * @param string $className * @param int $id */public static function exists ($className, $id) {$instance = Self::getinstance (); $key = "{$className}_{$id} "; if (Isset ($instance->all[$key]) {return $instance->all[$key];} return null;}}
So where do you mark it? there are, of course, Mapper::find (), Mapper::insert (), Mapper::createobject (), where query objects are generated. Addtomap () and Getfrommap () are added to the mapper. (other methods have not changed, so look to ignore it.) )

Mapper Code:

namespace Demo\mapper;use \demo\base\appexception;use \demo\base\applicationregistry;use \demo\domain\DomainObject Use \demo\domain\objectwatcher;/** * Mapper */abstract class Mapper {//pdoprotected static $PDO;//configprotected stat IC $dsn, $dbUserName, $dbPassword;//PDO option protected static $options = Array (\pdo::mysql_attr_init_command = =) SET NAMES UTF8 ', \pdo::attr_errmode, \pdo::errmode_exception,); Public Function __construct () {if (!isset (self:: $PDO)) {//Applicationregistry Get database connection Information $appregistry = Applicationregistry::getinstance (); self:: $dsn = $appRegistry->getdsn (); self:: $dbUserName = $appRegistry Getdbusername (); self:: $dbPassword = $appRegistry->getdbpassword (); if (!self:: $dsn | |!self:: $dbUserName | |!self:: $dbPassword) {throw new Appexception (' Mapper init failed! ');} Self:: $PDO = new \pdo (self:: $dsn, Self:: $dbUserName, Self:: $dbPassword, Self:: $options);}} /** * Find specified ID * @param int $id */public function FindByID ($id) {//get $obj from objectwatcher = $thiS->getfrommap ($id), if (!is_null ($obj)) {return $obj;} $pStmt = $this->getselectstmt (); $pStmt->execute (Array ($id)); $data = $pStmt->fetch (); $pStmt Closecursor (); if (!is_array ($data) | |!isset ($data [' id ']) {return $obj;} $obj = $this->createobject ($data); return $obj;} /** * Returns collection */public function FindAll () {$pStmt = $this->getselectallstmt (); $pStmt->execute (Array ()); $ Raws = $pStmt->fetchall (\PDO::FETCH_ASSOC), $collection = $this->getcollection ($raws); return $collection;} /** * Insert Data * @param \demo\domain\domainobject $obj */public function Insert (DomainObject $obj) {$flag = $this->doinsert ($obj);//Save or update Objectwatcher's $all[$key] object $this->addtomap ($obj); return $flag;} /** * Update Object * @param \demo\domain\domainobject $obj */public function Update (\demo\domain\domainobject $obj) {$flag = $th Is->doupdate ($obj); return $flag;} /** * Delete the specified ID * @param int $id */public function Deletebyid ($id) {$pStmt = $this->getdeletestmt (); $flag = $pStmt->exe Cute (ARRay ($id)); return $flag;} /** * Generates an object with a value attribute in $data * @param array $data */public function CreateObject (array $data) {//get objectwatcher from $obj = $this- >getfrommap ($data [' id ']), if (!is_null ($obj)) {return $obj;} Create Object $obj = $this->docreateobject ($data);//Add to Objectwatcher$this->addtomap ($obj); return $obj;} /** * Returns the object corresponding to the key tag * @param int $id */private function Getfrommap ($id) {return objectwatcher::exists ($this->gettargetc Lass (), $id);} /** * Add object to tag Map Objectwatcher class * @param domainobject $obj */private function Addtomap (DomainObject $obj) {return OBJECTWATC Her::add ($obj);} /** * Returns Subclass collection * @param array $raw */public function getcollection (array $raws) {return $this->getfactory ()->g Etcollection ($raws);} /** * Return subclass persistent Chemical Plant Object */public function GetFactory () {return persistancefactory::getfactory ($this->gettargetclass ());} protected abstract function Doinsert (\demo\domain\domainobject $obj);p rotected abstract function Docreateobject (array $DATA);p rotected abstract function getSelectstmt ();p rotected abstract function getselectallstmt ();p rotected abstract function doupdate (\demo\domain\ DomainObject $obj);p rotected abstract function getdeletestmt ();p rotected abstract function Gettargetclass ();}
Most of the code is before, only a small part of the change. Here is a picture:


Now, when mapper data from the database is mapped into objects that are tagged to objectwatcher, and you do not need to manually tag the object to Objectwatcher,mapper, you are done . The benefit is that you can reduce the operation of the database and the creation of new objects, such as Find, CreateObject. But this may be problematic, and if your program needs to process data concurrently, the tagged object data may be inconsistent, and you may need to lock the data at this point.


Unit of work

There are times when we may not change any value of the data but save the data to the database multiple times, which is of course unnecessary . The unit of work allows you to save only those objects that you need. The unit of work can save objects that have changed in this request to the database at the end of a request. at the end of a request, after the controller has called the command and view, we can let the unit of work perform the task here.

The purpose of tag mapping is to load unnecessary objects into the database at the beginning of the processing process, while the unit of work prevents unnecessary objects from being saved to the database after processing . These two ways of working are like complementary.

To determine which database operations are necessary, you need to keep track of the various events associated with the object (for example, setter () to reset the object's property values). It is of course best to keep track of the tracked objects.

Modified Objectwatcher class:

namespace Demo\domain;use \demo\domain\domainobject;/** * Tag map */class objectwatcher {private static $instance;//Tag Map PR Ivate $all = Array ();//Save new Object Private $new = Array ();//Save the Modified object ("Dirty object") Private $dirty = Array ();//Save Delete Object Private $delete = Array ();p rivate function __construct () {}public static function getinstance () {if (!isset (self:: $instance)) {self:: $in stance = new self ();} Return self:: $instance;} /** * Gets the key value corresponding to the object * @param domainobject $obj */public function Getgobalkey (DomainObject $obj) {$key = Get_class ($obj). '_' . $obj->getid (); return $key;} /** * Added to all * @param domainobject $obj */public static function Add (DomainObject $obj) {$instance = Self::getinstance (); $ Key = $instance->getgobalkey ($obj); $instance->all[$key] = $obj;} /** * Delete from all * @param domainobject $obj */public static function Delete (DomainObject $obj) {$instance = Self::getinstance (); $key = $instance->getgobalkey ($obj); unset ($instance->all[$key]);} /** * Added to new * @param domianobject $obj */publicstatic function AddNew (DomainObject $obj) {$instance = Self::getinstance (); $instance->new[] = $obj;} /** * Added to Dirty * @param domianobject $obj */public static function Adddirty (DomainObject $obj) {$instance = Self::getinsta NCE (); if (!in_array ($obj, $instance->dirty, True)) {$instance->dirty[$instance->getgobalkey ($obj)] = $obj;}} /** * Added to delete * @param domainobject $obj */public static function Adddelete (DomainObject $obj) {$instance = Self::getins Tance (); $instance->delete[$instance->getgobalkey ($obj)] = $obj;} /** * Clear Tag dirty new Delete * @param domainobject $obj */public static function Addclean (DomainObject $obj) {$instance = SE Lf::getinstance ();//unset Delete the Saved object unset ($instance->dirty[$instance->getgobalkey ($obj)]); Unset ($instance- >delete[$instance->getgobalkey ($obj)]);//Delete the object in new $instance->new = Array_filter ($instance->new, function ($a) use ($obj) {return! ( $a = = = $obj);});} /** * Determine if the tag exists * @param string $className * @param int $id */public static function exists ($className, $id) {$instance = Self::getinstance (); $key = "{$className}_{$id}"; if (Isset ($instance all[$key]) {return $instance->all[$key];} return null;} /** * Performs an operation on the tagged object in the new dirty delete */public function performoperations () {$instance = Self::getinstance ();//Newforeach ($ Instance->new as $obj) {$obj->finder ()->insert ($obj);} Dirtyforeach ($instance->dirty as $obj) {$obj->finder ()->update ($obj);} Deleteforeach ($instance->delete as $obj) {$obj->finder ()->delete ($obj);} $this->new = Array (), $this->dirty = Array (), $this->delete = Array ();}}
Objectwatcher is still a marker mapping, but here it adds the ability to track changes in the object in the system. The Objectwatcher class provides a mechanism for locating, deleting, and adding objects to the database.

because of the manipulation of objects on the Objectwatcher, it is appropriate for these objects to perform objectwatcher themselves.

Modified DomainObject classes (Marknew (), Markdirty (), Markdelete (), Markclean ()):

namespace demo\domain;use \demo\domain\helperfactory;use \demo\domain\objectwatcher /** * Domain Model abstract base class */abstract class DomainObject {protected $id = -1;public function __construct ($id = null) {if (Is_null ($i d) {//mark for new New $this->marknew ();} else {$this->id = $id;}} Public Function GetId () {return $this->id;} Public Function SetId ($id) {$this->id = $id; $this->markdirty ();} Public Function Marknew () {objectwatcher::addnew ($this);} Public Function Markdirty () {objectwatcher::adddirty ($this);} Public Function markdeleted () {objectwatcher::adddelete ($this);} Public Function Markclean () {Objectwatcher::addclean ($this);} public static function GetCollection ($type) {return helperfactory::getcollection ($type);} Public Function collection () {return self::getcollection (Get_class ($this));} public static function Getfinder ($type) {return helperfactory::getfinder ($type);} Public Function Finder () {return Self::getfinder (Get_class ($this));}} 
a diagram of DomainObject and Objectwatcher:

Modified mapper (and the same part above omitted, too occupy the seat):

/** * Mapper */abstract class Mapper {//.../** * find specified ID * @param int $id */public function FindByID ($id) {//from Objectwatch Er gets $obj = $this->getfrommap ($id), if (!is_null ($obj)) {return $obj;} $pStmt = $this->getselectstmt (); $pStmt->execute (Array ($id)); $data = $pStmt->fetch (); $pStmt Closecursor (); if (!is_array ($data) | |!isset ($data [' id ']) {return $obj;} $obj = $this->createobject ($data); return $obj;} /** * Insert Data * @param \demo\domain\domainobject $obj */public function Insert (DomainObject $obj) {$flag = $this->doinsert ($obj);//Save or update Objectwatcher $all[$key] Object $this->addtomap ($obj); $obj->markclean ();//debug with echo ' Insert: '. Get_class ($obj). '_' . $obj->getname (). '_' . $obj->getid (). '
'; return $flag;} /** * Update Object * @param \demo\domain\domainobject $obj */public function Update (\demo\domain\domainobject $obj) {$flag = $th Is->doupdate ($obj); $obj->markclean ();//debug with Echo ' Update: '. Get_class ($obj). '_' . $obj->getname (). '_' . $obj->getid (). '
'; return $flag;} /** * Generates an object with a value attribute in $data * @param array $data */public function CreateObject (array $data) {//get objectwatcher from $obj = $this- >getfrommap ($data [' id ']), if (!is_null ($obj)) {return $obj;} Create Object $obj = $this->docreateobject ($data);//Add to Objectwatcher$this->addtomap ($obj);//clear the new tag objectwatcher: : Addclean ($obj); return $obj;} //...}
you can see that the modified parts of mapper are events that have changed objects, that is, find (), update (), insert (), delete ().

Object changes can be tracked, where should the changed objects ("dirty data") be handled? As mentioned above, it should be when a request is nearing completion.

At the end of a request, the unit of work is called in the controller (also omitting the unchanged code):

Namespace demo\controller;/** * Controller */class Controller {//... private function handlereuqest () {$request = new \dem O\controller\request (); $appController = \demo\base\applicationregistry::getinstance ()->getappcontroller ();// After executing all command, there may be forwardwhile ($cmd = $appController->getcommand ($request)) {//Var_dump ($cmd); $cmd->execute ($request);//Set the current command as executed $request->setlastcommand ($cmd);} The work unit performs the task Objectwatcher::getinstance ()->performoperations ();//Get view $view = $appController->getview ($request );//Display View $this->invokeview ($view);} // ...}
objectwatcher::getinstance ()->performoperations ( )


OK, now let's use an example:

namespace Demo\command;use demo\domain\classroom;use Demo\base\applicationregistry;use demo\domain\objectwatcher; Use Demo\domain\helperfactory;class Test extends Command {protected function Doexecute (\demo\controller\request $ Request) {$crMapper = Helperfactory::getfinder (' demo\domain\classroom ');//Newly created object Marknew () $crA = new Classroom (); $ Cra->setname (' Four years (3) ');//Modified "dirty" data $CRB = $crMapper->findbyid (1); $crB->setname ("Five years (2)");}
Enter the URL:
Localhost/demo/runner.php?cmd=test
the output is the same as expected:
Insert:d emo\domain\classroom_ Four (3) class _58update:d emo\domain\classroom_ Five (2) class _1

Now the management of domain objects has been greatly improved. Also, the purpose of our use of patterns is to increase efficiency rather than reduce efficiency.


  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.