# IpTraceable behavior extension for Doctrine 2 **IpTraceable** behavior will automate the update of IP trace on your Entities or Documents. It works through annotations and can update fields on creation, update, property subset update, or even on specific property value change. This is very similar to Timestampable but sets a string. Note that you need to set the IP on the IpTraceableListener (unless you use the Symfony2 extension which does automatically assign the current request IP). Features: - Automatic predefined ip field update on creation, update, property subset update, and even on record property changes - ORM and ODM support using same listener - Specific annotations for properties, and no interface required - Can react to specific property or relation changes to specific value - Can be nested with other behaviors - Annotation, Yaml and Xml mapping support for extensions **Symfony:** - **IpTraceable** is not yet available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle) for **Symfony2**, together with all other extensions This article will cover the basic installation and functionality of **IpTraceable** behavior Content: - [Including](#including-extension) the extension - Entity [example](#entity-mapping) - Document [example](#document-mapping) - [Yaml](#yaml-mapping) mapping example - [Xml](#xml-mapping) mapping example - Advanced usage [examples](#advanced-examples) - Using [Traits](#traits) ## Setup and autoloading Read the [documentation](http://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/annotations.md#em-setup) or check the [example code](http://github.com/Atlantic18/DoctrineExtensions/tree/master/example) on how to setup and use the extensions in most optimized way. ## IpTraceable Entity example: ### IpTraceable annotations: - **@Gedmo\Mapping\Annotation\IpTraceable** this annotation tells that this column is ipTraceable by default it updates this column on update. If column is not a string field it will trigger an exception. Available configuration options: - **on** - is main option and can be **create, update, change** this tells when it should be updated - **field** - only valid if **on="change"** is specified, tracks property or a list of properties for changes - **value** - only valid if **on="change"** is specified and the tracked field is a single field (not an array), if the tracked field has this **value** then it updates the trace **Note:** that IpTraceable interface is not necessary, except in cases there you need to identify entity as being IpTraceable. The metadata is loaded only once then cache is activated Column is a string field: ``` php id; } public function setTitle($title) { $this->title = $title; } public function getTitle() { return $this->title; } public function setBody($body) { $this->body = $body; } public function getBody() { return $this->body; } public function getCreatedFromIp() { return $this->createdFromIp; } public function getUpdatedFromIp() { return $this->updatedFromIp; } public function getContentChangedFromIp() { return $this->contentChangedFromIp; } } ``` ## IpTraceable Document example: ``` php id; } public function setTitle($title) { $this->title = $title; } public function getTitle() { return $this->title; } public function getCreatedFromIp() { return $this->createdFromIp; } public function getUpdatedFromIp() { return $this->updatedFromIp; } } ``` Now on update and creation these annotated fields will be automatically updated ## Yaml mapping example: Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** ``` --- Entity\Article: type: entity table: articles id: id: type: integer generator: strategy: AUTO fields: title: type: string length: 64 createdFromIp: type: string length: 45 nullable: true gedmo: ipTraceable: on: create updatedFromIp: type: string length: 45 nullable: true gedmo: ipTraceable: on: update ``` ## Xml mapping example ``` xml ``` ## Advanced examples: ### Using dependency of property changes Add another entity which would represent Article Type: ``` php id; } public function setTitle($title) { $this->title = $title; } public function getTitle() { return $this->title; } } ``` Now update the Article Entity to reflect publishedFromIp on Type change: ``` php type = $type; } public function getId() { return $this->id; } public function setTitle($title) { $this->title = $title; } public function getTitle() { return $this->title; } public function getCreatedFromIp() { return $this->createdFromIp; } public function getUpdatedFromIp() { return $this->updatedFromIp; } public function getPublishedFromIp() { return $this->publishedFromIp; } } ``` Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml** ``` --- Entity\Article: type: entity table: articles id: id: type: integer generator: strategy: AUTO fields: title: type: string length: 64 createdFromIp: type: string length: 45 nullable: true gedmo: ipTraceable: on: create updatedFromIp: type: string length: 45 nullable: true gedmo: ipTraceable: on: update publishedFromIp: type: string length: 45 nullable: true gedmo: ipTraceable: on: change field: type.title value: Published manyToOne: type: targetEntity: Entity\Type inversedBy: articles ``` Now few operations to get it all done: ``` php setTitle('My Article'); $em->persist($article); $em->flush(); // article: $createdFromIp, $updatedFromIp were set $type = new Type; $type->setTitle('Published'); $article = $em->getRepository('Entity\Article')->findByTitle('My Article'); $article->setType($type); $em->persist($article); $em->persist($type); $em->flush(); // article: $publishedFromIp, $updatedFromIp were set $article->getPublishedFromIp(); // the IP that published this article ``` Easy like that, any suggestions on improvements are very welcome ## Traits You can use IpTraceable traits for quick **createdFromIp** **updatedFromIp** string definitions when using annotation mapping. There is also a trait without annotations for easy integration purposes. **Note:** this feature is only available since php **5.4.0**. And you are not required to use the Traits provided by extensions. ``` php ipTraceableListener = $ipTraceableListener; $this->request = $request; } /** * Set the username from the security context by listening on core.request * * @param GetResponseEvent $event */ public function onKernelRequest(GetResponseEvent $event) { if (null === $this->request) { return; } // If you use a cache like Varnish, you may want to set a proxy to Request::getClientIp() method // $this->request->setTrustedProxies(array('127.0.0.1')); // $ip = $_SERVER['REMOTE_ADDR']; $ip = $this->request->getClientIp(); if (null !== $ip) { $this->ipTraceableListener->setIpValue($ip); } } public static function getSubscribedEvents() { return array( KernelEvents::REQUEST => 'onKernelRequest', ); } } ``` ### Configuration for services.xml ``` xml Acme\DemoBundle\EventListener\IpTraceListener ... ```