init without trunk
This commit is contained in:
parent
ed24ac4994
commit
bb809e7233
14652 changed files with 177862 additions and 94817 deletions
1
vendor/gedmo/doctrine-extensions/.gitattributes
vendored
Normal file
1
vendor/gedmo/doctrine-extensions/.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/tests export-ignore
|
||||
8
vendor/gedmo/doctrine-extensions/.gitignore
vendored
Normal file
8
vendor/gedmo/doctrine-extensions/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
tests/phpunit.xml
|
||||
tests/temp/*.php
|
||||
tests/temp/*.log
|
||||
/vendor
|
||||
/bin
|
||||
/composer.lock
|
||||
/composer.phar
|
||||
.idea
|
||||
41
vendor/gedmo/doctrine-extensions/.travis.yml
vendored
Normal file
41
vendor/gedmo/doctrine-extensions/.travis.yml
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
dist: trusty
|
||||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: '5.4'
|
||||
env: phpunit_exclude_groups=datetimeinterface
|
||||
- php: '5.5'
|
||||
- php: '5.6'
|
||||
- php: '7.0'
|
||||
- php: '7.1'
|
||||
- php: '7.2'
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
|
||||
services: mongodb
|
||||
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_PHP_VERSION" = 5.* ]]; then echo 'extension=mongo.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != 5.* ]]; then echo 'extension=mongodb.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi
|
||||
- if [[ "$TRAVIS_PHP_VERSION" != 5.* ]]; then cp composer7.json composer.json; fi
|
||||
|
||||
install:
|
||||
- composer install --prefer-dist
|
||||
|
||||
script:
|
||||
- |
|
||||
if [[ ! $phpunit_exclude_groups ]]; then
|
||||
bin/phpunit -c tests/
|
||||
else
|
||||
bin/phpunit -c tests/ --exclude-group $phpunit_exclude_groups
|
||||
fi
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- gediminas.morkevicius@gmail.com
|
||||
- developers@atlantic18.com
|
||||
54
vendor/gedmo/doctrine-extensions/CHANGELOG.md
vendored
Normal file
54
vendor/gedmo/doctrine-extensions/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Doctrine Extensions Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
Each release should include sub-headers for the Extension above the types of
|
||||
changes, in order to more easily recognize how an Extension has changed in
|
||||
a release.
|
||||
|
||||
```
|
||||
## [2.4.36] - 2018-07-26
|
||||
### Sortable
|
||||
#### Fixed
|
||||
- Fix issue with add+delete position synchronization (#1932)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [2.4.39] - 2020-01-18
|
||||
### Tree
|
||||
### Fixed
|
||||
- The value of path source property is cast to string type for Materialized Path Tree strategy (#2061)
|
||||
|
||||
## [2.4.38] - 2019-11-08
|
||||
### Global / Shared
|
||||
#### Fixed
|
||||
- Add `parent::__construct()` calls to Listeners w/ custom constructors (#2012)
|
||||
- Add upcoming Doctrine ODM 2.0 to `composer.json` conflicts (#2027)
|
||||
|
||||
### Loggable
|
||||
#### Fixed
|
||||
- Added missing string casting of `objectId` in `LogEntryRepository::revert()` method (#2009)
|
||||
|
||||
### ReferenceIntegrity
|
||||
#### Fixed
|
||||
- Get class from meta in ReferenceIntegrityListener (#2021)
|
||||
|
||||
### Translatable
|
||||
#### Fixed
|
||||
- Return default AST executor instead of throwing Exception in Walker (#2018)
|
||||
- Fix duplicate inherited properties (#2029)
|
||||
|
||||
### Tree
|
||||
#### Fixed
|
||||
- Remove hard-coded parent column name in repository prev/next sibling queries (#2020)
|
||||
|
||||
## [2.4.37] - 2019-03-17
|
||||
### Translatable
|
||||
#### Fixed
|
||||
- Bugfix to load null value translations (#1990)
|
||||
38
vendor/gedmo/doctrine-extensions/CONTRIBUTING.md
vendored
Normal file
38
vendor/gedmo/doctrine-extensions/CONTRIBUTING.md
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Contributing to Doctrine Extensions
|
||||
|
||||
Thank you for your interest in contributing to Doctrine Extensions!
|
||||
|
||||
## Which Branch Should I Contribute To?
|
||||
|
||||
All pull requests should target the `master` branch, with a planned 3.0 major release.
|
||||
|
||||
:warning: The `v.2.4.x` branch has been marked as legacy/deprecated.
|
||||
|
||||
## Pull Request Titles
|
||||
|
||||
Please include the name(s) of the related extensions as a "tag" in the
|
||||
pull request title.
|
||||
|
||||
> [Tree] Add a new Oak Tree branching style
|
||||
|
||||
## Changelog
|
||||
|
||||
All updates must include an entry in the [Changelog](/changelog.md).
|
||||
Put your entry in the `[Unreleased]` section at the top, under the
|
||||
corresponding Extension and Category.
|
||||
|
||||
If there is a related GitHub issue, add it as a suffix to your change.
|
||||
|
||||
```
|
||||
## [Unreleased]
|
||||
### Loggable
|
||||
#### Fixed
|
||||
- Allow emoji in the docs (#123)
|
||||
```
|
||||
|
||||
## What You Can Contribute
|
||||
|
||||
Want to contribute but aren't sure where to start? Check out our
|
||||
[Issue Board](https://github.com/Atlantic18/DoctrineExtensions/issues)!
|
||||
There are lots of opportunities for helping other users with their issue,
|
||||
or contributing a reported bug fix or feature request.
|
||||
21
vendor/gedmo/doctrine-extensions/LICENSE
vendored
Normal file
21
vendor/gedmo/doctrine-extensions/LICENSE
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
Copyright (c) 2011-2015 Gediminas Morkevičius
|
||||
|
||||
The MIT license, reference http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
110
vendor/gedmo/doctrine-extensions/README.md
vendored
Normal file
110
vendor/gedmo/doctrine-extensions/README.md
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# Doctrine Behavioral Extensions
|
||||
|
||||
[](http://travis-ci.org/Atlantic18/DoctrineExtensions)
|
||||
[](https://packagist.org/packages/gedmo/doctrine-extensions)
|
||||
|
||||
This package contains extensions for Doctrine ORM and MongoDB ODM that offer new functionality or tools to use Doctrine
|
||||
more efficiently. These behaviors can be easily attached to the event system of Doctrine and handle the records being
|
||||
flushed in a behavioral way.
|
||||
|
||||
## Installation
|
||||
|
||||
$ composer require gedmo/doctrine-extensions
|
||||
|
||||
* [Symfony 2](/doc/symfony2.md)
|
||||
* [Symfony 4](/doc/symfony4.md)
|
||||
* [Laravel 5](https://www.laraveldoctrine.org/docs/1.3/extensions)
|
||||
* [Zend Framework 2](/doc/zendframework2.md)
|
||||
|
||||
## Extensions
|
||||
|
||||
#### ORM & MongoDB ODM
|
||||
|
||||
- [**Blameable**](/doc/blameable.md) - updates string or reference fields on create, update and even property change with a string or object (e.g. user).
|
||||
- [**Loggable**](/doc/loggable.md) - helps tracking changes and history of objects, also supports version management.
|
||||
- [**Sluggable**](/doc/sluggable.md) - urlizes your specified fields into single unique slug
|
||||
- [**Timestampable**](/doc/timestampable.md) - updates date fields on create, update and even property change.
|
||||
- [**Translatable**](/doc/translatable.md) - gives you a very handy solution for translating records into different languages. Easy to setup, easier to use.
|
||||
- [**Tree**](/doc/tree.md) - automates the tree handling process and adds some tree-specific functions on repository.
|
||||
(**closure**, **nested set** or **materialized path**)
|
||||
_(MongoDB ODM only supports materialized path)_
|
||||
|
||||
#### ORM Only
|
||||
|
||||
- [**IpTraceable**](/doc/ip_traceable.md) - inherited from Timestampable, sets IP address instead of timestamp
|
||||
- [**SoftDeleteable**](/doc/softdeleteable.md) - allows to implicitly remove records
|
||||
- [**Sortable**](/doc/sortable.md) - makes any document or entity sortable
|
||||
- [**Uploadable**](/doc/uploadable.md) - provides file upload handling in entity fields
|
||||
|
||||
#### MongoDB ODM Only
|
||||
|
||||
- [**References**](/doc/references.md) - supports linking Entities in Documents and vice versa
|
||||
- [**ReferenceIntegrity**](/doc/reference_integrity.md) - constrains ODM MongoDB Document references
|
||||
|
||||
All extensions support **YAML**, **Annotation** and **XML** mapping. Additional mapping drivers
|
||||
can be easily implemented using Mapping extension to handle the additional metadata mapping.
|
||||
|
||||
### Version Compatibility
|
||||
|
||||
| Extensions Version | Compatible Doctrine ORM & Common Library |
|
||||
| --- | --- |
|
||||
| 2.4 | 2.5+ |
|
||||
| 2.3 | 2.2 - 2.4 |
|
||||
|
||||
If you are setting up the Entity Manager without a framework, see the [the example](/example/em.php) to prevent issues like #1310
|
||||
|
||||
### XML Mapping
|
||||
|
||||
XML mapping needs to be in a different namespace, the declared namespace for
|
||||
Doctrine extensions is http://gediminasm.org/schemas/orm/doctrine-extensions-mapping
|
||||
So root node now looks like this:
|
||||
|
||||
```xml
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
...
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
XML mapping xsd schemas are also versioned and can be used by version suffix:
|
||||
|
||||
- Latest version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping**
|
||||
- 2.2.x version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping-2-2**
|
||||
- 2.1.x version - **http://gediminasm.org/schemas/orm/doctrine-extensions-mapping-2-1**
|
||||
|
||||
### Running Tests
|
||||
|
||||
**pdo-sqlite** extension is necessary.
|
||||
|
||||
To set up and run the tests, follow these steps:
|
||||
|
||||
- go to the root directory of extensions
|
||||
- download composer: `wget https://getcomposer.org/composer.phar`
|
||||
- install dev libraries: `php composer.phar install`
|
||||
- run: `bin/phpunit -c tests`
|
||||
- optional - run mongodb service if targeting mongo tests
|
||||
|
||||
### Running the Example
|
||||
|
||||
To set up and run example, follow these steps:
|
||||
|
||||
- go to the root directory of extensions
|
||||
- download composer: `wget https://getcomposer.org/composer.phar`
|
||||
- install dev libraries: `php composer.phar install`
|
||||
- edit `example/em.php` and configure your database on top of the file
|
||||
- run: `./example/bin/console` or `php example/bin/console` for console commands
|
||||
- run: `./example/bin/console orm:schema-tool:create` to create schema
|
||||
- run: `php example/run.php` to run example
|
||||
|
||||
### Contributors
|
||||
|
||||
Thanks to [everyone participating](http://github.com/l3pp4rd/DoctrineExtensions/contributors) in
|
||||
the development of these great Doctrine extensions!
|
||||
|
||||
And especially ones who create and maintain new extensions:
|
||||
|
||||
- Lukas Botsch [lbotsch](http://github.com/lbotsch)
|
||||
- Gustavo Adrian [comfortablynumb](http://github.com/comfortablynumb)
|
||||
- Boussekeyt Jules [gordonslondon](http://github.com/gordonslondon)
|
||||
- Kudryashov Konstantin [everzet](http://github.com/everzet)
|
||||
- David Buchmann [dbu](https://github.com/dbu)
|
||||
74
vendor/gedmo/doctrine-extensions/composer.json
vendored
Normal file
74
vendor/gedmo/doctrine-extensions/composer.json
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"name": "gedmo/doctrine-extensions",
|
||||
"type": "library",
|
||||
"description": "Doctrine2 behavioral extensions",
|
||||
"keywords": [
|
||||
"behaviors",
|
||||
"doctrine2",
|
||||
"extensions",
|
||||
"gedmo",
|
||||
"sluggable",
|
||||
"loggable",
|
||||
"translatable",
|
||||
"tree",
|
||||
"nestedset",
|
||||
"sortable",
|
||||
"timestampable",
|
||||
"blameable",
|
||||
"uploadable"
|
||||
],
|
||||
"homepage": "http://gediminasm.org/",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gediminas Morkevicius",
|
||||
"email": "gediminas.morkevicius@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Gustavo Falco",
|
||||
"email": "comfortablynumb84@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "David Buchmann",
|
||||
"email": "david@liip.ch"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"behat/transliterator": "~1.2",
|
||||
"doctrine/common": "~2.4"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.2",
|
||||
"doctrine/mongodb-odm": ">=2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/common": ">=2.5.0",
|
||||
"doctrine/mongodb-odm": ">=1.0.2 <2.0",
|
||||
"doctrine/orm": ">=2.5.0",
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5",
|
||||
"symfony/yaml": "^2.6 || ^3.0 || ^4.0 || ^5.0"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM",
|
||||
"doctrine/orm": "to use the extensions with the ORM"
|
||||
},
|
||||
"config": {
|
||||
"bin-dir": "bin",
|
||||
"sort-packages": true
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Gedmo\\": "lib/Gedmo"
|
||||
}
|
||||
},
|
||||
"support": {
|
||||
"email": "gediminas.morkevicius@gmail.com",
|
||||
"wiki": "https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc"
|
||||
}
|
||||
}
|
||||
77
vendor/gedmo/doctrine-extensions/composer7.json
vendored
Normal file
77
vendor/gedmo/doctrine-extensions/composer7.json
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
{
|
||||
"name": "gedmo/doctrine-extensions",
|
||||
"type": "library",
|
||||
"description": "Doctrine2 behavioral extensions",
|
||||
"keywords": [
|
||||
"behaviors",
|
||||
"doctrine2",
|
||||
"extensions",
|
||||
"gedmo",
|
||||
"sluggable",
|
||||
"loggable",
|
||||
"translatable",
|
||||
"tree",
|
||||
"nestedset",
|
||||
"sortable",
|
||||
"timestampable",
|
||||
"blameable",
|
||||
"uploadable"
|
||||
],
|
||||
"homepage": "http://gediminasm.org/",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Gediminas Morkevicius",
|
||||
"email": "gediminas.morkevicius@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Gustavo Falco",
|
||||
"email": "comfortablynumb84@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "David Buchmann",
|
||||
"email": "david@liip.ch"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"email": "gediminas.morkevicius@gmail.com",
|
||||
"wiki": "https://github.com/Atlantic18/DoctrineExtensions/tree/master/doc"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"behat/transliterator": "~1.2",
|
||||
"doctrine/common": "~2.4"
|
||||
},
|
||||
"provide": {
|
||||
"ext-mongo": "1.6.12"
|
||||
},
|
||||
"require-dev": {
|
||||
"alcaeus/mongo-php-adapter": "~1.0.4",
|
||||
"doctrine/mongodb-odm": ">=1.0.2 <2.0",
|
||||
"doctrine/orm": ">=2.5.0",
|
||||
"doctrine/common": ">=2.5.0",
|
||||
"symfony/yaml": "~2.6|~3.0|~4.0",
|
||||
"phpunit/phpunit": "^5.7|^6.5"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/annotations": "<1.2",
|
||||
"doctrine/mongodb": "<1.3",
|
||||
"doctrine/mongodb-odm": ">=2.0",
|
||||
"sebastian/comparator": "<2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/mongodb-odm": "to use the extensions with the MongoDB ODM",
|
||||
"doctrine/orm": "to use the extensions with the ORM"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Gedmo\\": "lib/Gedmo" }
|
||||
},
|
||||
"config": {
|
||||
"bin-dir": "bin"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
566
vendor/gedmo/doctrine-extensions/doc/annotations.md
vendored
Normal file
566
vendor/gedmo/doctrine-extensions/doc/annotations.md
vendored
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
# Annotation reference
|
||||
|
||||
Bellow you will find all annotation descriptions used in these extensions.
|
||||
There will be introduction on usage with examples. For more detailed usage
|
||||
on extensions, refer to their specific documentation.
|
||||
|
||||
Content:
|
||||
|
||||
- Best [practices](#em-setup) for setting up
|
||||
- [Tree](#gedmo-tree)
|
||||
- [Translatable](#gedmo-translatable)
|
||||
- [Sluggable](#gedmo-sluggable)
|
||||
- [Timestampable](#gedmo-timestampable)
|
||||
- [Loggable](#gedmo-loggable)
|
||||
|
||||
## Annotation mapping
|
||||
|
||||
Starting from **doctrine2.1.x** versions you have to import all used annotations
|
||||
by an **use** statement, see example bellow:
|
||||
|
||||
``` php
|
||||
namespace MyApp\Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo; // this will be like an alias for Gedmo extensions annotations
|
||||
use Doctrine\ORM\Mapping\Id; // includes single annotation
|
||||
use Doctrine\ORM\Mapping as ORM; // alias for doctrine ORM annotations
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\TranslationEntity(class="something")
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @Gedmo\Slug(fields={"title"}, updatable=false, separator="_")
|
||||
* @ORM\Column(length=32, unique=true)
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private $created;
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** this mapping applies only if you use **doctrine-common** library at version **2.1.x** or higher,
|
||||
extension library still supports old mapping styles if you manually set the mapping drivers
|
||||
|
||||
<a name="em-setup"></a>
|
||||
|
||||
## Best practices for setting up with annotations
|
||||
|
||||
New annotation reader does not depend on any namespaces, for that reason you can use
|
||||
single reader instance for whole project. The example bellow shows how to setup the
|
||||
mapping and listeners:
|
||||
|
||||
**Note:** using this repository you can test and check the [example demo configuration](https://github.com/Atlantic18/DoctrineExtensions/blob/master/example/em.php)
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// WARNING: setup, assumes that autoloaders are set
|
||||
|
||||
// globally used cache driver, in production use APC or memcached
|
||||
$cache = new Doctrine\Common\Cache\ArrayCache;
|
||||
// standard annotation reader
|
||||
$annotationReader = new Doctrine\Common\Annotations\AnnotationReader;
|
||||
$cachedAnnotationReader = new Doctrine\Common\Annotations\CachedReader(
|
||||
$annotationReader, // use reader
|
||||
$cache // and a cache driver
|
||||
);
|
||||
// create a driver chain for metadata reading
|
||||
$driverChain = new Doctrine\ORM\Mapping\Driver\DriverChain();
|
||||
// load superclass metadata mapping only, into driver chain
|
||||
// also registers Gedmo annotations.NOTE: you can personalize it
|
||||
Gedmo\DoctrineExtensions::registerAbstractMappingIntoDriverChainORM(
|
||||
$driverChain, // our metadata driver chain, to hook into
|
||||
$cachedAnnotationReader // our cached annotation reader
|
||||
);
|
||||
|
||||
// now we want to register our application entities,
|
||||
// for that we need another metadata driver used for Entity namespace
|
||||
$annotationDriver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver(
|
||||
$cachedAnnotationReader, // our cached annotation reader
|
||||
array(__DIR__.'/app/Entity') // paths to look in
|
||||
);
|
||||
// NOTE: driver for application Entity can be different, Yaml, Xml or whatever
|
||||
// register annotation driver for our application Entity namespace
|
||||
$driverChain->addDriver($annotationDriver, 'Entity');
|
||||
|
||||
// general ORM configuration
|
||||
$config = new Doctrine\ORM\Configuration;
|
||||
$config->setProxyDir(sys_get_temp_dir());
|
||||
$config->setProxyNamespace('Proxy');
|
||||
$config->setAutoGenerateProxyClasses(false); // this can be based on production config.
|
||||
// register metadata driver
|
||||
$config->setMetadataDriverImpl($driverChain);
|
||||
// use our already initialized cache driver
|
||||
$config->setMetadataCacheImpl($cache);
|
||||
$config->setQueryCacheImpl($cache);
|
||||
|
||||
// create event manager and hook preferred extension listeners
|
||||
$evm = new Doctrine\Common\EventManager();
|
||||
// gedmo extension listeners, remove which are not used
|
||||
|
||||
// sluggable
|
||||
$sluggableListener = new Gedmo\Sluggable\SluggableListener;
|
||||
// you should set the used annotation reader to listener, to avoid creating new one for mapping drivers
|
||||
$sluggableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($sluggableListener);
|
||||
|
||||
// tree
|
||||
$treeListener = new Gedmo\Tree\TreeListener;
|
||||
$treeListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($treeListener);
|
||||
|
||||
// loggable, not used in example
|
||||
$loggableListener = new Gedmo\Loggable\LoggableListener;
|
||||
$loggableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($loggableListener);
|
||||
|
||||
// timestampable
|
||||
$timestampableListener = new Gedmo\Timestampable\TimestampableListener;
|
||||
$timestampableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($timestampableListener);
|
||||
|
||||
// translatable
|
||||
$translatableListener = new Gedmo\Translatable\TranslatableListener;
|
||||
// current translation locale should be set from session or hook later into the listener
|
||||
// most important, before entity manager is flushed
|
||||
$translatableListener->setTranslatableLocale('en');
|
||||
$translatableListener->setDefaultLocale('en');
|
||||
$translatableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($translatableListener);
|
||||
|
||||
// sortable, not used in example
|
||||
$sortableListener = new Gedmo\Sortable\SortableListener;
|
||||
$sortableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($sortableListener);
|
||||
|
||||
// mysql set names UTF-8 if required
|
||||
$evm->addEventSubscriber(new Doctrine\DBAL\Event\Listeners\MysqlSessionInit());
|
||||
// DBAL connection
|
||||
$connection = array(
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => '127.0.0.1',
|
||||
'dbname' => 'test',
|
||||
'user' => 'root',
|
||||
'password' => ''
|
||||
);
|
||||
// Finally, create entity manager
|
||||
$em = Doctrine\ORM\EntityManager::create($connection, $config, $evm);
|
||||
```
|
||||
|
||||
**Note:** that symfony2 StofDoctrineExtensionsBundle does it automatically this
|
||||
way you will maintain a single instance of annotation reader. It relates only
|
||||
to doctrine-common-2.1.x branch and newer.
|
||||
|
||||
<a name="gedmo-tree"></a>
|
||||
|
||||
## Tree annotations
|
||||
|
||||
Tree can use different adapters. Currently **Tree** extension supports **NestedSet**
|
||||
and **Closure** strategies which has a difference for annotations used. Note, that
|
||||
tree will automatically map indexes which are considered necessary for best performance.
|
||||
|
||||
### @Gedmo\Mapping\Annotation\Tree (required for all tree strategies)
|
||||
|
||||
**class** annotation
|
||||
|
||||
Is the main identificator of tree used for domain object which should **act as Tree**.
|
||||
|
||||
**options:**
|
||||
|
||||
- **type** - (string) _optional_ default: **nested**
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Tree(type="nested")
|
||||
* @Doctrine\ORM\Mapping\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
|
||||
*/
|
||||
class Category ...
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\TreeParent (required for all tree strategies)
|
||||
|
||||
**property** annotation
|
||||
|
||||
This annotation forces to specify the **parent** field, which must be a **ManyToOne**
|
||||
relation
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\TreeParent
|
||||
* @Doctrine\ORM\Mapping\ManyToOne(targetEntity="Category")
|
||||
* @Doctrine\ORM\Mapping\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
private $parent;
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\TreeLeft (required for nested tree)
|
||||
|
||||
**property** annotation
|
||||
|
||||
This annotation forces to specify the **left** field, which will be used for generation
|
||||
of nestedset left values. Property must be **integer** type.
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\TreeLeft
|
||||
* @Doctrine\ORM\Mapping\Column(type=integer)
|
||||
*/
|
||||
private $lft;
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\TreeRight (required for nested tree)
|
||||
|
||||
**property** annotation
|
||||
|
||||
This annotation forces to specify the **right** field, which will be used for generation
|
||||
of nestedset right values. Property must be **integer** type.
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\TreeRight
|
||||
* @Doctrine\ORM\Mapping\Column(type=integer)
|
||||
*/
|
||||
private $rgt;
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\TreeRoot (optional for nested tree)
|
||||
|
||||
**property** annotation
|
||||
|
||||
This annotation will use **integer** type field to specify the root of tree. This way
|
||||
updating tree will cost less because each root will act as separate tree.
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\TreeRoot
|
||||
* @Doctrine\ORM\Mapping\Column(type=integer, nullable=true)
|
||||
*/
|
||||
private $root;
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\TreeLevel (optional for nested tree)
|
||||
|
||||
**property** annotation
|
||||
|
||||
This annotation lets to store the **level** of each node in the tree, in other word it
|
||||
is depth. Can be used for indentation for instance. Property must be **integer** type.
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\TreeLevel
|
||||
* @Doctrine\ORM\Mapping\Column(type=integer)
|
||||
*/
|
||||
private $lvl;
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\TreeClosure (required for closure tree)
|
||||
|
||||
**class** annotation
|
||||
|
||||
This annotation forces to specify the closure domain object, which must
|
||||
extend **AbstractClosure** in order to have personal closures.
|
||||
|
||||
**options:**
|
||||
|
||||
- **class** - (string) _required_
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Tree(type="closure")
|
||||
* @Gedmo\Mapping\Annotation\TreeClosure(class="Entity\CategoryClosure")
|
||||
* @Doctrine\ORM\Mapping\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\ClosureTreeRepository")
|
||||
*/
|
||||
class Category ...
|
||||
```
|
||||
|
||||
<a name="gedmo-translatable"></a>
|
||||
|
||||
## Translatable annotations
|
||||
|
||||
Translatable additionally can have unmapped property, which would override the
|
||||
locale used by listener.
|
||||
|
||||
### @Gedmo\Mapping\Annotation\TranslationEntity (optional)
|
||||
|
||||
**class** annotation
|
||||
|
||||
This class annotation can force translatable to use **personal Entity** to store
|
||||
translations. In large tables this can be very handy.
|
||||
|
||||
**options:**
|
||||
|
||||
- **class** - (string) _required_
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\TranslationEntity(class="Entity\ProductTranslation")
|
||||
* @Doctrine\ORM\Mapping\Entity
|
||||
*/
|
||||
class Product ...
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\Translatable (required in order to translate)
|
||||
|
||||
**property** annotation
|
||||
|
||||
This annotation simply marks **any type** of field to be tracked and translated into
|
||||
currently used locale. Locale can be forced through entity or set by **TranslationListener**.
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Translatable
|
||||
* @Doctrine\ORM\Mapping\Column(type=text)
|
||||
*/
|
||||
private $content;
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\Locale or @Gedmo\Mapping\Annotation\Language (optional)
|
||||
|
||||
**unmapped property** annotation
|
||||
|
||||
Both annotations will do exactly the same - mark property as one which can override
|
||||
the locale set by **TranslationListener**. Property must not be mapped, that means
|
||||
it cannot be stored in database.
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Locale
|
||||
*/
|
||||
private $locale;
|
||||
```
|
||||
|
||||
<a name="gedmo-sluggable"></a>
|
||||
|
||||
## Sluggable annotations
|
||||
|
||||
Sluggable ensures unique slugs and correct length of the slug. It also uses utf8 to ascii
|
||||
table map to create correct ascii slugs.
|
||||
|
||||
### @Gedmo\Mapping\Annotation\Slug (required)
|
||||
|
||||
**property** annotation
|
||||
|
||||
It will use this **string** property to store the generated slug.
|
||||
|
||||
**options:**
|
||||
|
||||
- **fields** - (array) _required_, must at least contain one field
|
||||
- **updatable** - (boolean) _optional_ default: **true**
|
||||
- **separator** - (string) _optional_ default: **-**
|
||||
- **unique** - (boolean) _optional_ default: **true**
|
||||
- **style** - (string) _optional_ default: **default** lowercase, can be **camel** also
|
||||
- **handlers** - (array) _optional_ default: empty array, refer to the documentation below, possible elements: **Gedmo\Mapping\Annotation\SlugHandler**
|
||||
|
||||
### Slug handlers:
|
||||
|
||||
- Gedmo\Sluggable\Handler\TreeSlugHandler - transforms a tree slug into path based, example "food/fruits/apricots/king-apricots"
|
||||
- Gedmo\Sluggable\Handler\RelativeSlugHandler - takes a relation slug and prefixes the slug, example "singers/michael-jackson"
|
||||
in order to synchronize updates regarding the relation changes, you will need to hood **InversedRelativeSlugHandler** to the relation mentioned.
|
||||
- Gedmo\Sluggable\Handler\InversedRelativeSlugHandler - updates prefixed slug for an inversed relation which is mapped by **RelativeSlugHandler**
|
||||
|
||||
examples:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Slug
|
||||
* @Doctrine\ORM\Mapping\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
```
|
||||
|
||||
with TreeSlugHandler
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Slug(handlers={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandler(class="Gedmo\Sluggable\Handler\TreeSlugHandler", options={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="parentRelationField", value="parent"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="separator", value="/")
|
||||
* })
|
||||
* }, separator="-", updatable=true)
|
||||
* @Doctrine\ORM\Mapping\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
```
|
||||
|
||||
with **RelativeSlugHandler**:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* Person domain object class
|
||||
*
|
||||
* @Gedmo\Mapping\Annotation\Slug(handlers={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandler(class="Gedmo\Sluggable\Handler\RelativeSlugHandler", options={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="relationField", value="category"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="relationSlugField", value="slug"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="separator", value="/")
|
||||
* })
|
||||
* })
|
||||
* @Doctrine\ORM\Mapping\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
```
|
||||
|
||||
if you used **RelativeSlugHandler** - relation object should use **InversedRelativeSlugHandler**:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* Category domain object class
|
||||
*
|
||||
* @Gedmo\Mapping\Annotation\Slug(handlers={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandler(class="Gedmo\Sluggable\Handler\InversedRelativeSlugHandler", options={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="relationClass", value="App\Entity\Person"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="mappedBy", value="category"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="inverseSlugField", value="slug")
|
||||
* })
|
||||
* })
|
||||
* @Doctrine\ORM\Mapping\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
```
|
||||
|
||||
<a name="gedmo-timestampable"></a>
|
||||
|
||||
## Timestampable annotations
|
||||
|
||||
Timestampable will update date fields on create, update or property change. If you set/force
|
||||
date manually it will not update it.
|
||||
|
||||
### @Gedmo\Mapping\Annotation\Timestampable (required)
|
||||
|
||||
**property** annotation
|
||||
|
||||
Marks a **date, datetime or time** field as timestampable.
|
||||
|
||||
**options:**
|
||||
|
||||
- **on** - (string) _optional_ default: **update**, other choice is **create** or **change**
|
||||
- **field** - (string) _conditional_ required only if it triggers on **change**, name of the **field**
|
||||
or if it is a relation **property.field**
|
||||
- **value** - (mixed) _conditional_ required only if it triggers on **change**, value of property
|
||||
which would trigger an update.
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Timestampable(on="create")
|
||||
* @Doctrine\ORM\Mapping\Column(type="datetime")
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Timestampable(on="change", field="status.title", value="Published")
|
||||
* @Doctrine\ORM\Mapping\Column(type="date")
|
||||
*/
|
||||
private $published;
|
||||
|
||||
/**
|
||||
* @Doctrine\ORM\Mapping\ManyToOne(targetEntity="Status")
|
||||
*/
|
||||
private $status;
|
||||
```
|
||||
|
||||
<a name="gedmo-loggable"></a>
|
||||
|
||||
## Loggable annotations
|
||||
|
||||
Loggable is used to log all actions made on annotated object class, it logs insert, update
|
||||
and remove actions for a username which currently is logged in for instance.
|
||||
Further more, it stores all **Versioned** property changes in the log which allows
|
||||
a version management implementation for this object.
|
||||
|
||||
### @Gedmo\Mapping\Annotation\Loggable (required)
|
||||
|
||||
**class** annotation
|
||||
|
||||
This class annotation marks object as being loggable and logs all actions being done to
|
||||
this class records.
|
||||
|
||||
**options:**
|
||||
|
||||
- **logEntryClass** - (string) _optional_ personal log storage class
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Loggable(logEntryClass="Entity\ProductLogEntry")
|
||||
* @Doctrine\ORM\Mapping\Entity
|
||||
*/
|
||||
class Product ...
|
||||
```
|
||||
|
||||
### @Gedmo\Mapping\Annotation\Versioned (optional)
|
||||
|
||||
**property** annotation
|
||||
|
||||
Tracks the marked property for changes to be logged, can be set to single valued associations
|
||||
but not for collections. Using these log entries as revisions, objects can be reverted to
|
||||
a specific version.
|
||||
|
||||
example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Versioned
|
||||
* @Doctrine\ORM\Mapping\Column(type="text")
|
||||
*/
|
||||
private $content;
|
||||
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Versioned
|
||||
* @Doctrine\ORM\Mapping\ManyToOne(targetEntity="Article", inversedBy="comments")
|
||||
*/
|
||||
private $article;
|
||||
```
|
||||
644
vendor/gedmo/doctrine-extensions/doc/blameable.md
vendored
Normal file
644
vendor/gedmo/doctrine-extensions/doc/blameable.md
vendored
Normal file
|
|
@ -0,0 +1,644 @@
|
|||
# Blameable behavior extension for Doctrine 2
|
||||
|
||||
**Blameable** behavior will automate the update of username or user reference fields
|
||||
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 or user object for a user association.
|
||||
|
||||
If you map the blame onto a string field, this extension will try to assign the user name.
|
||||
If you map the blame onto a association field, this extension will try to assign the user
|
||||
object to it.
|
||||
|
||||
Note that you need to set the user on the BlameableListener (unless you use the
|
||||
Symfony2 extension which does automatically assign the current security context
|
||||
user).
|
||||
|
||||
|
||||
Features:
|
||||
|
||||
- Automatic predefined user 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:**
|
||||
|
||||
- **Blameable** is 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 **Blameable** 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)
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
## Blameable Entity example:
|
||||
|
||||
### Blameable annotations:
|
||||
- **@Gedmo\Mapping\Annotation\Blameable** this annotation tells that this column is blameable
|
||||
by default it updates this column on update. If column is not a string field or an association
|
||||
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 blame
|
||||
|
||||
**Note:** that Blameable interface is not necessary, except in cases there
|
||||
you need to identify entity as being Blameable. The metadata is loaded only once then
|
||||
cache is activated
|
||||
|
||||
Column is a string field:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="body", type="string")
|
||||
*/
|
||||
private $body;
|
||||
|
||||
/**
|
||||
* @var string $createdBy
|
||||
*
|
||||
* @Gedmo\Blameable(on="create")
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private $createdBy;
|
||||
|
||||
/**
|
||||
* @var string $updatedBy
|
||||
*
|
||||
* @Gedmo\Blameable(on="update")
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private $updatedBy;
|
||||
|
||||
/**
|
||||
* @var string $contentChangedBy
|
||||
*
|
||||
* @ORM\Column(name="content_changed_by", type="string", nullable=true)
|
||||
* @Gedmo\Blameable(on="change", field={"title", "body"})
|
||||
*/
|
||||
private $contentChangedBy;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->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 getCreatedBy()
|
||||
{
|
||||
return $this->createdBy;
|
||||
}
|
||||
|
||||
public function getUpdatedBy()
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
|
||||
public function getContentChangedBy()
|
||||
{
|
||||
return $this->contentChangedBy;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Column is an association:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ODM\String
|
||||
*/
|
||||
private $body;
|
||||
|
||||
/**
|
||||
* @var User $createdBy
|
||||
*
|
||||
* @Gedmo\Blameable(on="create")
|
||||
* @ORM\ManyToOne(targetEntity="Path\To\Entity\User")
|
||||
* @ORM\JoinColumn(name="created_by", referencedColumnName="id")
|
||||
*/
|
||||
private $createdBy;
|
||||
|
||||
/**
|
||||
* @var User $updatedBy
|
||||
*
|
||||
* @Gedmo\Blameable(on="update")
|
||||
* @ORM\ManyToOne(targetEntity="Path\To\Entity\User")
|
||||
* @ORM\JoinColumn(name="updated_by", referencedColumnName="id")
|
||||
*/
|
||||
private $updatedBy;
|
||||
|
||||
/**
|
||||
* @var User $contentChangedBy
|
||||
*
|
||||
* @Gedmo\Blameable(on="change", fields={"title", "body"})
|
||||
* @ORM\ManyToOne(targetEntity="Path\To\Entity\User")
|
||||
* @ORM\JoinColumn(name="content_changed_by", referencedColumnName="id")
|
||||
*/
|
||||
private $contentChangedBy;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->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 getCreatedBy()
|
||||
{
|
||||
return $this->createdBy;
|
||||
}
|
||||
|
||||
public function getUpdatedBy()
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
|
||||
public function getContentChangedBy()
|
||||
{
|
||||
return $this->contentChangedBy;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="document-mapping"></a>
|
||||
|
||||
## Blameable Document example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Document;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
|
||||
/**
|
||||
* @ODM\Document(collection="articles")
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ODM\Id */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string $createdBy
|
||||
*
|
||||
* @ODM\Field(type="string")
|
||||
* @Gedmo\Blameable(on="create")
|
||||
*/
|
||||
private $createdBy;
|
||||
|
||||
/**
|
||||
* @var string $updatedBy
|
||||
*
|
||||
* @ODM\Field(type="string")
|
||||
* @Gedmo\Blameable
|
||||
*/
|
||||
private $updatedBy;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getCreatedBy()
|
||||
{
|
||||
return $this->createdBy;
|
||||
}
|
||||
|
||||
public function getUpdatedBy()
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now on update and creation these annotated fields will be automatically updated
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## 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
|
||||
createdBy:
|
||||
type: string
|
||||
gedmo:
|
||||
blameable:
|
||||
on: create
|
||||
updatedBy:
|
||||
type: string
|
||||
gedmo:
|
||||
blameable:
|
||||
on: update
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
|
||||
<entity name="Mapping\Fixture\Xml\Blameable" table="blameables">
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="createdBy" type="string">
|
||||
<gedmo:blameable on="create"/>
|
||||
</field>
|
||||
<field name="updatedBy" type="string">
|
||||
<gedmo:blameable on="update"/>
|
||||
</field>
|
||||
<field name="publishedBy" type="string" nullable="true">
|
||||
<gedmo:blameable on="change" field="status.title" value="Published"/>
|
||||
</field>
|
||||
|
||||
<many-to-one field="status" target-entity="Status">
|
||||
<join-column name="status_id" referenced-column-name="id"/>
|
||||
</many-to-one>
|
||||
</entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="advanced-examples"></a>
|
||||
|
||||
## Advanced examples:
|
||||
|
||||
### Using dependency of property changes
|
||||
|
||||
Add another entity which would represent Article Type:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Type
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Article", mappedBy="type")
|
||||
*/
|
||||
private $articles;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now update the Article Entity to reflect publishedBy on Type change:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string $createdBy
|
||||
*
|
||||
* @Gedmo\Blameable(on="create")
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private $createdBy;
|
||||
|
||||
/**
|
||||
* @var string $updatedBy
|
||||
*
|
||||
* @Gedmo\Blameable(on="update")
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private $updatedBy;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Type", inversedBy="articles")
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var string $publishedBy
|
||||
*
|
||||
* @ORM\Column(type="string", nullable=true)
|
||||
* @Gedmo\Blameable(on="change", field="type.title", value="Published")
|
||||
*/
|
||||
private $publishedBy;
|
||||
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getCreatedBy()
|
||||
{
|
||||
return $this->createdBy;
|
||||
}
|
||||
|
||||
public function getUpdatedBy()
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
|
||||
public function getPublishedBy()
|
||||
{
|
||||
return $this->publishedBy;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
createdBy:
|
||||
type: string
|
||||
gedmo:
|
||||
blameable:
|
||||
on: create
|
||||
updatedBy:
|
||||
type: string
|
||||
gedmo:
|
||||
blameable:
|
||||
on: update
|
||||
publishedBy:
|
||||
type: string
|
||||
gedmo:
|
||||
blameable:
|
||||
on: change
|
||||
field: type.title
|
||||
value: Published
|
||||
manyToOne:
|
||||
type:
|
||||
targetEntity: Entity\Type
|
||||
inversedBy: articles
|
||||
```
|
||||
|
||||
Now few operations to get it all done:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Article;
|
||||
$article->setTitle('My Article');
|
||||
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
// article: $createdBy, $updatedBy 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: $publishedBy, $updatedBy were set
|
||||
|
||||
$article->getPublishedBy(); // the user that published this article
|
||||
```
|
||||
|
||||
Easy like that, any suggestions on improvements are very welcome
|
||||
|
||||
|
||||
<a name="traits"></a>
|
||||
|
||||
## Traits
|
||||
|
||||
You can use blameable traits for quick **createdBy** **updatedBy** 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
|
||||
<?php
|
||||
namespace Blameable\Fixture;
|
||||
|
||||
use Gedmo\Blameable\Traits\BlameableEntity;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class UsingTrait
|
||||
{
|
||||
/**
|
||||
* Hook blameable behavior
|
||||
* updates createdBy, updatedBy fields
|
||||
*/
|
||||
use BlameableEntity;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=128)
|
||||
*/
|
||||
private $title;
|
||||
}
|
||||
```
|
||||
|
||||
The Traits are very simplistic - if you use different field names it is recommended to simply create your
|
||||
own Traits specific to your project. The ones provided by this bundle can be used as example.
|
||||
660
vendor/gedmo/doctrine-extensions/doc/ip_traceable.md
vendored
Normal file
660
vendor/gedmo/doctrine-extensions/doc/ip_traceable.md
vendored
Normal file
|
|
@ -0,0 +1,660 @@
|
|||
# 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)
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
## 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
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="body", type="string")
|
||||
*/
|
||||
private $body;
|
||||
|
||||
/**
|
||||
* @var string $createdFromIp
|
||||
*
|
||||
* @Gedmo\IpTraceable(on="create")
|
||||
* @ORM\Column(type="string", length=45, nullable=true)
|
||||
*/
|
||||
private $createdFromIp;
|
||||
|
||||
/**
|
||||
* @var string $updatedFromIp
|
||||
*
|
||||
* @Gedmo\IpTraceable(on="update")
|
||||
* @ORM\Column(type="string", length=45, nullable=true)
|
||||
*/
|
||||
private $updatedFromIp;
|
||||
|
||||
/**
|
||||
* @var datetime $contentChangedFromIp
|
||||
*
|
||||
* @ORM\Column(name="content_changed_by", type="string", nullable=true, length=45)
|
||||
* @Gedmo\IpTraceable(on="change", field={"title", "body"})
|
||||
*/
|
||||
private $contentChangedFromIp;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<a name="document-mapping"></a>
|
||||
|
||||
## IpTraceable Document example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Document;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
|
||||
/**
|
||||
* @ODM\Document(collection="articles")
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ODM\Id */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string $createdFromIp
|
||||
*
|
||||
* @ODM\Field(type="string")
|
||||
* @Gedmo\IpTraceable(on="create")
|
||||
*/
|
||||
private $createdFromIp;
|
||||
|
||||
/**
|
||||
* @var string $updatedFromIp
|
||||
*
|
||||
* @ODM\Field(type="string")
|
||||
* @Gedmo\IpTraceable
|
||||
*/
|
||||
private $updatedFromIp;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now on update and creation these annotated fields will be automatically updated
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
|
||||
<entity name="Mapping\Fixture\Xml\IpTraceable" table="ip-traceable">
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="createdFromIp" type="string", length="45", nullable="true">
|
||||
<gedmo:ip-traceable on="create"/>
|
||||
</field>
|
||||
<field name="updatedFromIp" type="string", length="45", nullable="true">
|
||||
<gedmo:ip-traceable on="update"/>
|
||||
</field>
|
||||
<field name="publishedFromIp" type="string" nullable="true", length="45">
|
||||
<gedmo:ip-traceable on="change" field="status.title" value="Published"/>
|
||||
</field>
|
||||
|
||||
<many-to-one field="status" target-entity="Status">
|
||||
<join-column name="status_id" referenced-column-name="id"/>
|
||||
</many-to-one>
|
||||
</entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="advanced-examples"></a>
|
||||
|
||||
## Advanced examples:
|
||||
|
||||
### Using dependency of property changes
|
||||
|
||||
Add another entity which would represent Article Type:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Type
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Article", mappedBy="type")
|
||||
*/
|
||||
private $articles;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->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
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var string $createdFromIp
|
||||
*
|
||||
* @Gedmo\IpTraceable(on="create")
|
||||
* @ORM\Column(type="string", length=45, nullable=true)
|
||||
*/
|
||||
private $createdFromIp;
|
||||
|
||||
/**
|
||||
* @var string $updatedFromIp
|
||||
*
|
||||
* @Gedmo\IpTraceable(on="update")
|
||||
* @ORM\Column(type="string", length=45, nullable=true)
|
||||
*/
|
||||
private $updatedFromIp;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Type", inversedFromIp="articles")
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var string $publishedFromIp
|
||||
*
|
||||
* @ORM\Column(type="string", nullable=true, length=45)
|
||||
* @Gedmo\IpTraceable(on="change", field="type.title", value="Published")
|
||||
*/
|
||||
private $publishedFromIp;
|
||||
|
||||
public function setType($type)
|
||||
{
|
||||
$this->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
|
||||
<?php
|
||||
$article = new Article;
|
||||
$article->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
|
||||
|
||||
|
||||
<a name="traits"></a>
|
||||
|
||||
## 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
|
||||
<?php
|
||||
namespace IpTraceable\Fixture;
|
||||
|
||||
use Gedmo\IpTraceable\Traits\IpTraceableEntity;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class UsingTrait
|
||||
{
|
||||
/**
|
||||
* Hook ip-traceable behavior
|
||||
* updates createdFromIp, updatedFromIp fields
|
||||
*/
|
||||
use IpTraceableEntity;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=128)
|
||||
*/
|
||||
private $title;
|
||||
}
|
||||
```
|
||||
|
||||
The Traits are very simplistic - if you use different field names it is recommended to simply create your
|
||||
own Traits specific to your project. The ones provided by this bundle can be used as example.
|
||||
|
||||
|
||||
## Example of implementation in Symfony2
|
||||
|
||||
In your Sf2 application, declare an event subscriber that automatically set IP value on IpTraceableListener.
|
||||
|
||||
### Code of subscriber class
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
namespace Acme\DemoBundle\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
use Gedmo\IpTraceable\IpTraceableListener;
|
||||
|
||||
/**
|
||||
* IpTraceSubscriber
|
||||
*/
|
||||
class IpTraceSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @var IpTraceableListener
|
||||
*/
|
||||
private $ipTraceableListener;
|
||||
|
||||
public function __construct(IpTraceableListener $ipTraceableListener, Request $request = null)
|
||||
{
|
||||
$this->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
|
||||
<?xml version="1.0" ?>
|
||||
|
||||
<container xmlns="http://symfony.com/schema/dic/services"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
|
||||
|
||||
<parameters>
|
||||
<parameter key="alterphp_doctrine_extensions.event_listener.ip_trace.class">Acme\DemoBundle\EventListener\IpTraceListener</parameter>
|
||||
</parameters>
|
||||
|
||||
<services>
|
||||
|
||||
...
|
||||
|
||||
<service id="gedmo_doctrine_extensions.listener.ip_traceable" class="Gedmo\IpTraceable\IpTraceableListener" public="false">
|
||||
<tag name="doctrine.event_subscriber" connection="default" />
|
||||
<call method="setAnnotationReader">
|
||||
<argument type="service" id="annotation_reader" />
|
||||
</call>
|
||||
</service>
|
||||
|
||||
<service id="alterphp_doctrine_extensions.event_listener.ip_trace" class="%alterphp_doctrine_extensions.event_listener.ip_trace.class%" public="false" scope="request">
|
||||
<argument type="service" id="gedmo_doctrine_extensions.listener.ip_traceable" />
|
||||
<argument type="service" id="request" on-invalid="null" />
|
||||
<tag name="kernel.event_subscriber" />
|
||||
</service>
|
||||
|
||||
</services>
|
||||
</container>
|
||||
|
||||
```
|
||||
266
vendor/gedmo/doctrine-extensions/doc/loggable.md
vendored
Normal file
266
vendor/gedmo/doctrine-extensions/doc/loggable.md
vendored
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
# Loggable behavioral extension for Doctrine2
|
||||
|
||||
**Loggable** behavior tracks your record changes and is able to
|
||||
manage versions.
|
||||
|
||||
Features:
|
||||
|
||||
- Automatic storage of log entries in database
|
||||
- ORM and ODM support using same listener
|
||||
- Can be nested with other behaviors
|
||||
- Objects can be reverted to previous versions
|
||||
- Annotation, Yaml and Xml mapping support for extensions
|
||||
|
||||
Update **2011-04-04**
|
||||
|
||||
- Made single listener, one instance can be used for any object manager
|
||||
and any number of them
|
||||
|
||||
**Portability:**
|
||||
|
||||
- **Loggable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle)
|
||||
ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions
|
||||
|
||||
This article will cover the basic installation and functionality of **Loggable**
|
||||
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
|
||||
- Basic usage [examples](#basic-examples)
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
### Loggable annotations:
|
||||
|
||||
- **@Gedmo\Mapping\Annotation\Loggable(logEntryClass="my\class")** this class annotation
|
||||
will store logs to optionally specified **logEntryClass**. You will still need to specify versioned fields with the following annotation.
|
||||
- **@Gedmo\Mapping\Annotation\Versioned** tracks annotated property for changes
|
||||
|
||||
### Loggable username:
|
||||
|
||||
In order to set the username, when adding the loggable listener you need to set it this way:
|
||||
|
||||
``` php
|
||||
$loggableListener = new Gedmo\Loggable\LoggableListener;
|
||||
$loggableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$loggableListener->setUsername('admin');
|
||||
$evm->addEventSubscriber($loggableListener);
|
||||
```
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
## Loggable Entity example:
|
||||
|
||||
**Note:** that Loggable interface is not necessary, except in cases there
|
||||
you need to identify entity as being Loggable. The metadata is loaded only once when
|
||||
cache is active
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\Loggable
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Versioned
|
||||
* @ORM\Column(name="title", type="string", length=8)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="document-mapping"></a>
|
||||
|
||||
## Loggable Document example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Document;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
|
||||
/**
|
||||
* @ODM\Document(collection="articles")
|
||||
* @Gedmo\Loggable
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ODM\Id */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
* @Gedmo\Versioned
|
||||
*/
|
||||
private $title;
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## Yaml mapping example
|
||||
|
||||
Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
|
||||
|
||||
```
|
||||
---
|
||||
Entity\Article:
|
||||
type: entity
|
||||
table: articles
|
||||
gedmo:
|
||||
loggable:
|
||||
# using specific personal LogEntryClass class:
|
||||
logEntryClass: My\LogEntry
|
||||
# without specifying the LogEntryClass class:
|
||||
# loggable: true
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
title:
|
||||
type: string
|
||||
length: 64
|
||||
gedmo:
|
||||
- versioned
|
||||
content:
|
||||
type: text
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
|
||||
<entity name="Mapping\Fixture\Xml\Loggable" table="loggables">
|
||||
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="title" type="string" length="128">
|
||||
<gedmo:versioned/>
|
||||
</field>
|
||||
<many-to-one field="status" target-entity="Status">
|
||||
<join-column name="status_id" referenced-column-name="id"/>
|
||||
<gedmo:versioned/>
|
||||
</many-to-one>
|
||||
|
||||
<gedmo:loggable log-entry-class="Gedmo\Loggable\Entity\LogEntry"/>
|
||||
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="basic-examples"></a>
|
||||
|
||||
## Basic usage examples:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Entity\Article;
|
||||
$article->setTitle('my title');
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
This inserted an article and inserted the logEntry for it, which contains
|
||||
all new changeset. In case if there is **OneToOne or ManyToOne** relation,
|
||||
it will store only identifier of that object to avoid storing proxies
|
||||
|
||||
Now lets update our article:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// first load the article
|
||||
$article = $em->find('Entity\Article', 1 /*article id*/);
|
||||
$article->setTitle('my new title');
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
This updated an article and inserted the logEntry for update action with new changeset
|
||||
Now lets revert it to previous version:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// first check our log entries
|
||||
$repo = $em->getRepository('Gedmo\Loggable\Entity\LogEntry'); // we use default log entry class
|
||||
$article = $em->find('Entity\Article', 1 /*article id*/);
|
||||
$logs = $repo->getLogEntries($article);
|
||||
/* $logs contains 2 logEntries */
|
||||
// lets revert to first version
|
||||
$repo->revert($article, 1/*version*/);
|
||||
// notice article is not persisted yet, you need to persist and flush it
|
||||
echo $article->getTitle(); // prints "my title"
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
// if article had changed relation, it would be reverted also.
|
||||
```
|
||||
|
||||
Easy like that, any suggestions on improvements are very welcome
|
||||
491
vendor/gedmo/doctrine-extensions/doc/mapping.md
vendored
Normal file
491
vendor/gedmo/doctrine-extensions/doc/mapping.md
vendored
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
# Mapping extension for Doctrine2
|
||||
|
||||
**Mapping** extension makes it easy to map additional metadata for event listeners.
|
||||
It supports **Yaml**, **Xml** and **Annotation** drivers which will be chosen depending on
|
||||
currently used mapping driver for your domain objects. **Mapping** extension also
|
||||
provides abstraction layer of **EventArgs** to make it possible to use single listener
|
||||
for different object managers like **ODM** and **ORM**.
|
||||
|
||||
Features:
|
||||
|
||||
- Mapping drivers for annotation and yaml
|
||||
- Conventional extension points for metadata extraction and object manager abstraction
|
||||
|
||||
- Public [Mapping repository](http://github.com/Atlantic18/DoctrineExtensions "Mapping extension on Github") is available on github
|
||||
- Last update date: **2012-01-02**
|
||||
|
||||
This article will cover the basic installation and usage of **Mapping** extension
|
||||
|
||||
Content:
|
||||
|
||||
- [Including](#including-extension) the extension
|
||||
- [Creating](#create-extension) an extension
|
||||
- Defining [annotations](#annotations)
|
||||
- Creating [listener](#create-listener)
|
||||
- Attaching our [listener](#attach-listener) to the event manager
|
||||
- [Entity](#entity-mapping) with some fields to encode
|
||||
- Adapting listener to support [different](#different-managers) object managers
|
||||
- [Customizing](#event-adapter-customize) event adapter for specific functions
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
<a name="create-extension"></a>
|
||||
|
||||
## Tutorial on creation of mapped extension
|
||||
|
||||
First, lets assume we will use **Extension** namespace for our additional
|
||||
extension library. You should create an **Extension** directory in your library
|
||||
or vendor directory. After some changes your project might look like:
|
||||
|
||||
```
|
||||
project
|
||||
...
|
||||
bootstrap.php
|
||||
vendor
|
||||
Extension
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
Now you can use any namespace autoloader class and register this namespace. We
|
||||
will use Doctrine\Common\ClassLoader for instance:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// path is related to bootstrap.php location for example
|
||||
$classLoader = new \Doctrine\Common\ClassLoader('Extension', "vendor");
|
||||
$classLoader->register();
|
||||
```
|
||||
|
||||
Now lets create some files which are necessary for our extension:
|
||||
|
||||
```
|
||||
project
|
||||
...
|
||||
bootstrap.php
|
||||
vendor
|
||||
Extension
|
||||
Encoder
|
||||
Mapping
|
||||
Driver
|
||||
Annotation.php
|
||||
Annotations.php
|
||||
EncoderListener.php
|
||||
...
|
||||
```
|
||||
|
||||
**Note:** that extension will look for mapping in **ExtensionNamespace/Mapping**
|
||||
directory. And **Driver** directory should be named as Driver. These are the conventions
|
||||
of **Mapping** extension.
|
||||
|
||||
That is all we will need for now. As you may noticed we will create an encoding
|
||||
listener which could encode your fields by specified annotations. In real life it
|
||||
may not be useful since object will not know how to match the value.
|
||||
|
||||
<a name="annotations"></a>
|
||||
|
||||
## Now lets define available annotations and setup drivers
|
||||
|
||||
Edit **Annotations.php** file:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// file: vendor/Extension/Encoder/Mapping/Annotations.php
|
||||
|
||||
namespace Extension\Encoder\Mapping;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
final class Encode extends Annotation
|
||||
{
|
||||
public $type = 'md5';
|
||||
public $secret;
|
||||
}
|
||||
```
|
||||
|
||||
Edit **Annotation.php** driver file:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// file: vendor/Extension/Encoder/Mapping/Driver/Annotation.php
|
||||
|
||||
namespace Extension\Encoder\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
|
||||
class Annotation implements Driver
|
||||
{
|
||||
public function readExtendedMetadata($meta, array &$config) {
|
||||
// load our available annotations
|
||||
require_once __DIR__ . '/../Annotations.php';
|
||||
$reader = new AnnotationReader();
|
||||
|
||||
$class = $meta->getReflectionClass();
|
||||
// check only property annotations
|
||||
foreach ($class->getProperties() as $property) {
|
||||
// skip inherited properties
|
||||
if ($meta->isMappedSuperclass && !$property->isPrivate() ||
|
||||
$meta->isInheritedField($property->name) ||
|
||||
isset($meta->associationMappings[$property->name]['inherited'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
// now lets check if property has our annotation
|
||||
if ($encode = $reader->getPropertyAnnotation($property, 'Extension\Encoder\Mapping\Encode')) {
|
||||
$field = $property->getName();
|
||||
// check if field is mapped
|
||||
if (!$meta->hasField($field)) {
|
||||
throw new \Exception("Field is not mapped as object property");
|
||||
}
|
||||
// allow encoding only strings
|
||||
if (!in_array($encode->type, array('sha1', 'md5'))) {
|
||||
throw new \Exception("Invalid encoding type supplied");
|
||||
}
|
||||
// validate encoding type
|
||||
$mapping = $meta->getFieldMapping($field);
|
||||
if ($mapping['type'] != 'string') {
|
||||
throw new \Exception("Only strings can be encoded");
|
||||
}
|
||||
// store the metadata
|
||||
$config['encode'][$field] = array(
|
||||
'type' => $encode->type,
|
||||
'secret' => $encode->secret
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="create-listener"></a>
|
||||
|
||||
## Finally, lets create the listener
|
||||
|
||||
**Note:** this version of listener will support only ORM Entities
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// file: vendor/Extension/Encoder/EncoderListener.php
|
||||
|
||||
namespace Extension\Encoder;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Gedmo\Mapping\MappedEventSubscriber;
|
||||
|
||||
class EncoderListener extends MappedEventSubscriber
|
||||
{
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onFlush',
|
||||
'loadClassMetadata'
|
||||
);
|
||||
}
|
||||
|
||||
public function loadClassMetadata(EventArgs $args)
|
||||
{
|
||||
// this will check for our metadata
|
||||
$this->loadMetadataForObjectClass(
|
||||
$args->getEntityManager(),
|
||||
$args->getClassMetadata()
|
||||
);
|
||||
}
|
||||
|
||||
public function onFlush(EventArgs $args)
|
||||
{
|
||||
$em = $args->getEntityManager();
|
||||
$uow = $em->getUnitOfWork();
|
||||
|
||||
// check all pending updates
|
||||
foreach ($uow->getScheduledEntityUpdates() as $object) {
|
||||
$meta = $em->getClassMetadata(get_class($object));
|
||||
// if it has our metadata lets encode the properties
|
||||
if ($config = $this->getConfiguration($em, $meta->name)) {
|
||||
$this->encode($em, $object, $config);
|
||||
}
|
||||
}
|
||||
// check all pending insertions
|
||||
foreach ($uow->getScheduledEntityInsertions() as $object) {
|
||||
$meta = $em->getClassMetadata(get_class($object));
|
||||
// if it has our metadata lets encode the properties
|
||||
if ($config = $this->getConfiguration($em, $meta->name)) {
|
||||
$this->encode($em, $object, $config);
|
||||
}
|
||||
// recalculate changeset
|
||||
$em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $object);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getNamespace()
|
||||
{
|
||||
// mapper must know the namespace of extension
|
||||
return __NAMESPACE__;
|
||||
}
|
||||
|
||||
private function encode($em, $object, $config)
|
||||
{
|
||||
$meta = $em->getClassMetadata(get_class($object));
|
||||
foreach ($config['encode'] as $field => $options) {
|
||||
$value = $meta->getReflectionProperty($field)->getValue($object);
|
||||
$method = $options['type'];
|
||||
$encoded = $method($options['secret'].$value);
|
||||
$meta->getReflectionProperty($field)->setValue($object, $encoded);
|
||||
}
|
||||
// recalculate changeset
|
||||
$em->getUnitOfWork()->recomputeSingleEntityChangeSet($meta, $object);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Our **Encoder** extension is ready, now if we want to test it, we need
|
||||
to attach our **EncoderListener** to the EventManager and create an entity
|
||||
with some fields to encode.
|
||||
|
||||
<a name="attach-listener"></a>
|
||||
|
||||
### Attaching the EncoderListener
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$evm = new \Doctrine\Common\EventManager();
|
||||
$encoderListener = new \Extension\Encoder\EncoderListener;
|
||||
$evm->addEventSubscriber($encoderListener);
|
||||
// now this event manager should be passed to entity manager constructor
|
||||
```
|
||||
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
### Create an entity with some fields to encode
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace YourNamespace\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Extension\Encoder\Mapping as EXT;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="test_users")
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @EXT\Encode(type="sha1", secret="xxx")
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @EXT\Encode(type="md5")
|
||||
* @ORM\Column(length=32)
|
||||
*/
|
||||
private $password;
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password;
|
||||
}
|
||||
|
||||
public function getPassword()
|
||||
{
|
||||
return $this->password;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you will try to create a new **User** you will get encoded fields in database.
|
||||
|
||||
<a name="different-managers"></a>
|
||||
|
||||
## Adapting listener to support other object managers
|
||||
|
||||
Now the event adapter comes into play, lets slightly modify our listener:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// file: vendor/Extension/Encoder/EncoderListener.php
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Gedmo\Mapping\MappedEventSubscriber;
|
||||
use Gedmo\Mapping\Event\AdapterInterface as EventAdapterInterface;
|
||||
|
||||
class EncoderListener extends MappedEventSubscriber
|
||||
{
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onFlush',
|
||||
'loadClassMetadata'
|
||||
);
|
||||
}
|
||||
|
||||
public function loadClassMetadata(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
// this will check for our metadata
|
||||
$this->loadMetadataForObjectClass(
|
||||
$ea->getObjectManager(),
|
||||
$args->getClassMetadata()
|
||||
);
|
||||
}
|
||||
|
||||
public function onFlush(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
$om = $ea->getObjectManager();
|
||||
$uow = $om->getUnitOfWork();
|
||||
|
||||
// check all pending updates
|
||||
foreach ($ea->getScheduledObjectUpdates($uow) as $object) {
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
// if it has our metadata lets encode the properties
|
||||
if ($config = $this->getConfiguration($om, $meta->name)) {
|
||||
$this->encode($ea, $object, $config);
|
||||
}
|
||||
}
|
||||
// check all pending insertions
|
||||
foreach ($ea->getScheduledObjectInsertions($uow) as $object) {
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
// if it has our metadata lets encode the properties
|
||||
if ($config = $this->getConfiguration($om, $meta->name)) {
|
||||
$this->encode($ea, $object, $config);
|
||||
}
|
||||
// recalculate changeset
|
||||
$ea->recomputeSingleObjectChangeSet($uow, $meta, $object);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getNamespace()
|
||||
{
|
||||
// mapper must know the namespace of extension
|
||||
return __NAMESPACE__;
|
||||
}
|
||||
|
||||
private function encode(EventAdapterInterface $ea, $object, $config)
|
||||
{
|
||||
$om = $ea->getObjectManager();
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
$uow = $om->getUnitOfWork();
|
||||
foreach ($config['encode'] as $field => $options) {
|
||||
$value = $meta->getReflectionProperty($field)->getValue($object);
|
||||
$method = $options['type'];
|
||||
$encoded = $method($options['secret'].$value);
|
||||
$meta->getReflectionProperty($field)->setValue($object, $encoded);
|
||||
}
|
||||
// recalculate changeset
|
||||
$ea->recomputeSingleObjectChangeSet($uow, $meta, $object);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** event adapter uses **EventArgs** to recognize with which manager
|
||||
we are dealing with. It also uses event arguments to retrieve manager and transforms
|
||||
the method call in its way. You can extend the event adapter in order to add some
|
||||
specific methods for each manager.
|
||||
|
||||
That's it, now it will work on ORM and ODM object managers.
|
||||
|
||||
<a name="event-adapter-customize"></a>
|
||||
|
||||
## Customizing event adapter for specific functions
|
||||
|
||||
In most cases event listener will need specific functionality which will differ
|
||||
for every object manager. For instance, a query to load users will differ. The
|
||||
example bellow will illustrate how to handle such situations. You will need to
|
||||
extend default ORM and ODM event adapters to implement specific functions which
|
||||
will be available through the event adapter. First we will need to follow the
|
||||
mapping convention to use those extension points.
|
||||
|
||||
### Extending default event adapters
|
||||
|
||||
Update your directory structure:
|
||||
|
||||
```
|
||||
project
|
||||
...
|
||||
bootstrap.php
|
||||
vendor
|
||||
Extension
|
||||
Encoder
|
||||
Mapping
|
||||
Driver
|
||||
Annotation.php
|
||||
Event
|
||||
Adapter
|
||||
ORM.php
|
||||
ODM.php
|
||||
Annotations.php
|
||||
EncoderListener.php
|
||||
...
|
||||
```
|
||||
|
||||
Now **Mapping** extension will automatically create event adapter instances
|
||||
from the extended ones.
|
||||
|
||||
Create extended ORM event adapter:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// file: vendor/Extension/Encoder/Mapping/Event/Adapter/ORM.php
|
||||
|
||||
namespace Extension\Encoder\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
|
||||
|
||||
class ORM extends BaseAdapterORM
|
||||
{
|
||||
public function someSpecificMethod()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Create extended ODM event adapter:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// file: vendor/Extension/Encoder/Mapping/Event/Adapter/ODM.php
|
||||
|
||||
namespace Extension\Encoder\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ODM as BaseAdapterODM;
|
||||
|
||||
class ODM extends BaseAdapterODM
|
||||
{
|
||||
public function someSpecificMethod()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It would be useful to make a common interface for those extended adapters.
|
||||
Now every possible requirement is fulfilled and this may be useful.
|
||||
|
||||
Any suggestions on improvements are very welcome
|
||||
171
vendor/gedmo/doctrine-extensions/doc/reference_integrity.md
vendored
Normal file
171
vendor/gedmo/doctrine-extensions/doc/reference_integrity.md
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
# Reference Integrity behavior extension for Doctrine 2
|
||||
|
||||
**ReferenceIntegrity** behavior will automate the reference integrity for referenced documents.
|
||||
It works through annotations and yaml, and supports 'nullify', 'pull' and 'restrict' which throws an exception.
|
||||
|
||||
So let's say you have a Type which is referenced to multiple Articles, when deleting the Type, by default the Article
|
||||
would still have a reference to Type, since Mongo doesn't care. When setting the ReferenceIntegrity to 'nullify' it
|
||||
would then automatically remove the reference from Article.
|
||||
|
||||
When the owning side (Article#types) is a ReferenceMany and ReferenceIntegrity is set to 'pull', the removed document would automatically be pulled from Article#types.
|
||||
|
||||
Features:
|
||||
|
||||
- Automatically remove referenced association
|
||||
- ODM only
|
||||
- ReferenceOne and ReferenceMany support
|
||||
- 'nullify', 'pull' and 'restrict' support
|
||||
- Annotation and Yaml mapping support for extensions
|
||||
|
||||
|
||||
**Symfony:**
|
||||
|
||||
- **ReferenceIntegrity** is 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 **ReferenceIntegrity** behavior
|
||||
|
||||
Content:
|
||||
|
||||
- [Including](#including-extension) the extension
|
||||
- Document [example](#document-mapping)
|
||||
- [Yaml](#yaml-mapping) mapping example
|
||||
- Usage [examples](#advanced-examples)
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
<a name="document-mapping"></a>
|
||||
|
||||
## ReferenceIntegrity Document example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Document;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* @ODM\Document(collection="types")
|
||||
*/
|
||||
class Type
|
||||
{
|
||||
/**
|
||||
* @ODM\Id
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ODM\ReferenceOne(targetDocument="Article", mappedBy="type")
|
||||
* @Gedmo\ReferenceIntegrity("nullify")
|
||||
* @var Article
|
||||
*/
|
||||
protected $article;
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
It is necessary to have the 'mappedBy' option set, to be able to access the referenced documents.
|
||||
On removal of Type, on the referenced Article the Type reference will be nullified (removed)
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## Yaml mapping example:
|
||||
|
||||
Yaml mapped Article: **/mapping/yaml/Documents.Article.dcm.yml**
|
||||
|
||||
```
|
||||
---
|
||||
Document\Type:
|
||||
type: document
|
||||
collection: types
|
||||
fields:
|
||||
id:
|
||||
id: true
|
||||
title:
|
||||
type: string
|
||||
article:
|
||||
reference: true
|
||||
type: one
|
||||
mappedBy: type
|
||||
targetDocument: Document\Article
|
||||
gedmo:
|
||||
referenceIntegrity: nullify # or pull or restrict
|
||||
|
||||
```
|
||||
|
||||
It is necessary to have the 'mappedBy' option set, to be able to access the referenced documents.
|
||||
|
||||
<a name="advanced-examples"></a>
|
||||
|
||||
## Usage examples:
|
||||
|
||||
Few operations to see 'nullify' in action:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Article;
|
||||
$article->setTitle('My Article');
|
||||
|
||||
$type = new Type;
|
||||
$type->setTitle('Published');
|
||||
|
||||
$article->setType($type);
|
||||
|
||||
$em->persist($article);
|
||||
$em->persist($type);
|
||||
$em->flush();
|
||||
|
||||
$type = $em->getRepository('Document\Type')->findByTitle('Published');
|
||||
$em->remove($type);
|
||||
$em->flush();
|
||||
|
||||
$article = $em->getRepository('Document\Article')->findByTitle('My Article');
|
||||
$article->getType(); // won't be referenced to Type anymore
|
||||
```
|
||||
|
||||
Few operations to see 'pull' in action:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Article;
|
||||
$article->setTitle('My Article');
|
||||
|
||||
$type1 = new Type;
|
||||
$type1->setTitle('Published');
|
||||
|
||||
$type2 = new Type;
|
||||
$type2->setTitle('Info');
|
||||
|
||||
$article->addType($type1);
|
||||
$article->addType($type2);
|
||||
|
||||
$em->persist($article);
|
||||
$em->persist($type1);
|
||||
$em->persist($type2);
|
||||
$em->flush();
|
||||
|
||||
$type2 = $em->getRepository('Document\Type')->findByTitle('Info');
|
||||
$em->remove($type2);
|
||||
$em->flush();
|
||||
|
||||
$article = $em->getRepository('Document\Article')->findByTitle('My Article');
|
||||
$article->getTypes(); // will only contain $type1 ('Published')
|
||||
```
|
||||
|
||||
When 'ReferenceIntegrity' is set to 'restrict' a `ReferenceIntegrityStrictException` will be thrown, only when there
|
||||
is a referenced document.
|
||||
|
||||
Easy like that, any suggestions on improvements are very welcome
|
||||
219
vendor/gedmo/doctrine-extensions/doc/references.md
vendored
Normal file
219
vendor/gedmo/doctrine-extensions/doc/references.md
vendored
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
# Cross Object Mapper References behavior extension for Doctrine 2
|
||||
|
||||
Create documents and entities that contain references to each other.
|
||||
|
||||
## Options
|
||||
|
||||
The following options are possible on reference one and many associations:
|
||||
|
||||
**Owning Side**
|
||||
|
||||
- **type** - The type of association.
|
||||
- **class** - The associated class name.
|
||||
- **inversedBy** - The property name for the inverse side of this association.
|
||||
- **identifier** - The property name to store the associated object id in.
|
||||
|
||||
**Inverse Side**
|
||||
|
||||
- **type** - The type of association.
|
||||
- **class** - The associated class name.
|
||||
- **mappedBy** - The property name for the owning side of this association.
|
||||
|
||||
## Annotations
|
||||
|
||||
**@Gedmo\ReferenceMany**
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @Gedmo\ReferenceMany(type="entity", class="Entity\StockItem", mappedBy="product")
|
||||
*/
|
||||
private $stockItems;
|
||||
```
|
||||
|
||||
**@Gedmo\ReferenceOne**
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @Gedmo\ReferenceOne(type="document", class="Document\Product", inversedBy="stockItems", identifier="productId")
|
||||
*/
|
||||
private $product;
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
Here is an example where you have a Product which is mapped using the Doctrine MongoDB ODM project and it contains a property `$stockItems` that is populated from the Doctrine2 ORM.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
namespace Document;
|
||||
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* @ODM\Document
|
||||
*/
|
||||
class Product
|
||||
{
|
||||
/**
|
||||
* @ODM\Id
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @Gedmo\ReferenceMany(type="entity", class="Entity\StockItem", mappedBy="product")
|
||||
*/
|
||||
private $stockItems;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getStockItems()
|
||||
{
|
||||
return $this->stockItems;
|
||||
}
|
||||
|
||||
public function setStockItems(Collection $stockItems)
|
||||
{
|
||||
$this->stockItems = $stockItems;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `StockItem` has a reference to the `Product` as well.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use References\Fixture\ODM\MongoDB\Product;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class StockItem
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @ORM\Column
|
||||
*/
|
||||
private $sku;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $quantity;
|
||||
|
||||
/**
|
||||
* @Gedmo\ReferenceOne(type="document", class="Document\Product", inversedBy="stockItems", identifier="productId")
|
||||
*/
|
||||
private $product;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private $productId;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getSku()
|
||||
{
|
||||
return $this->sku;
|
||||
}
|
||||
|
||||
public function setSku($sku)
|
||||
{
|
||||
$this->sku = $sku;
|
||||
}
|
||||
|
||||
public function getQuantity()
|
||||
{
|
||||
return $this->quantity;
|
||||
}
|
||||
|
||||
public function setQuantity($quantity)
|
||||
{
|
||||
$this->quantity = $quantity;
|
||||
}
|
||||
|
||||
public function setProduct(Product $product)
|
||||
{
|
||||
$this->product = $product;
|
||||
}
|
||||
|
||||
public function getProduct()
|
||||
{
|
||||
return $this->product;
|
||||
}
|
||||
|
||||
public function setProductId($productId)
|
||||
{
|
||||
$this->productId = $productId;
|
||||
}
|
||||
|
||||
public function getProductId()
|
||||
{
|
||||
return $this->productId;
|
||||
}
|
||||
}
|
||||
```
|
||||
810
vendor/gedmo/doctrine-extensions/doc/sluggable.md
vendored
Normal file
810
vendor/gedmo/doctrine-extensions/doc/sluggable.md
vendored
Normal file
|
|
@ -0,0 +1,810 @@
|
|||
# Sluggable behavior extension for Doctrine 2
|
||||
|
||||
**Sluggable** behavior will build the slug of predefined fields on a given field
|
||||
which should store the slug
|
||||
|
||||
Features:
|
||||
|
||||
- Automatic predefined field transformation into slug
|
||||
- ORM and ODM support using same listener
|
||||
- Slugs can be unique and styled, even with prefixes and/or suffixes
|
||||
- Can be nested with other behaviors
|
||||
- Annotation, Yaml and Xml mapping support for extensions
|
||||
- Multiple slugs, different slugs can link to same fields
|
||||
|
||||
Update **2013-10-26**
|
||||
|
||||
- Datetime support with default dateFormat Y-m-d-H:i
|
||||
|
||||
Update **2013-08-23**
|
||||
|
||||
- Added 'prefix' and 'suffix' configuration parameter #812
|
||||
|
||||
Update **2013-08-19**
|
||||
|
||||
- allow empty slug #807 regenerate slug only if set to `null`
|
||||
|
||||
Update **2013-03-10**
|
||||
|
||||
- Added 'unique_base' configuration parameter to the Sluggable behaviour
|
||||
|
||||
Update **2012-11-30**
|
||||
|
||||
- Recreated slug handlers, as they are used by many people
|
||||
|
||||
Update **2012-02-26**
|
||||
|
||||
- Remove slug handlers were removed because of complications it brought together
|
||||
|
||||
|
||||
Update **2011-09-11**
|
||||
|
||||
- Refactored sluggable for doctrine2.2 by specifying slug fields directly in slug annotation
|
||||
- Slug handler functionality, possibility to create custom ones or use built-in
|
||||
tree path handler or linked slug through single valued association
|
||||
- Updated documentation mapping examples for 2.1.x version or higher
|
||||
|
||||
Update **2011-04-04**
|
||||
|
||||
- Made single listener, one instance can be used for any object manager and any number of them
|
||||
|
||||
Update **2010-12-23**
|
||||
|
||||
- Full support for unique index on slug field,
|
||||
no more exceptions during concurrent flushes.
|
||||
|
||||
**Note:**
|
||||
|
||||
- There is a reported [issue](https://github.com/Atlantic18/DoctrineExtensions/issues/254) that sluggable transliterator
|
||||
does not work on OSX 10.6 its ok starting again from 10.7 version. To overcome the problem
|
||||
you can use your [custom transliterator](#transliterator)
|
||||
- Public [Sluggable repository](http://github.com/Atlantic18/DoctrineExtensions "Sluggable extension on Github") is available on github
|
||||
- Last update date: **2012-02-26**
|
||||
- For usage together with **SoftDeleteable** in order to take into account softdeleted entities while generating unique
|
||||
slug, you must explicitly call **addManagedFilter** with a name of softdeleteable filter, so it can be disabled during
|
||||
slug updates. The best place to do it, is when initializing sluggable listener. That will be automated in the future.
|
||||
|
||||
**Portability:**
|
||||
|
||||
- **Sluggable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle)
|
||||
ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions
|
||||
|
||||
This article will cover the basic installation and functionality of **Sluggable**
|
||||
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
|
||||
- Basic usage [examples](#basic-examples)
|
||||
- Custom [transliterator](#transliterator)
|
||||
- Advanced usage [examples](#advanced-examples)
|
||||
- Using [slug handlers](#slug-handlers)
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
## Sluggable Entity example:
|
||||
|
||||
### Sluggable annotations:
|
||||
|
||||
- **@Gedmo\Mapping\Annotation\Slug** it will use this column to store **slug** generated
|
||||
**fields** option must be specified, an array of field names to slug
|
||||
|
||||
**Note:** that Sluggable interface is not necessary, except in cases there
|
||||
you need to identify entity as being Sluggable. The metadata is loaded only once then
|
||||
cache is activated
|
||||
|
||||
**Note:** 2.0.x version of extensions used @Gedmo\Mapping\Annotation\Sluggable to identify
|
||||
the field for slug
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="articles")
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=16)
|
||||
*/
|
||||
private $code;
|
||||
|
||||
/**
|
||||
* @Gedmo\Slug(fields={"title", "code"})
|
||||
* @ORM\Column(length=128, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getSlug()
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="document-mapping"></a>
|
||||
|
||||
## Sluggable Document example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Document;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* @ODM\Document(collection="articles")
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/**
|
||||
* @ODM\Id
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $code;
|
||||
|
||||
/**
|
||||
* @Gedmo\Slug(fields={"title", "code"})
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $slug;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getSlug()
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## 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
|
||||
code:
|
||||
type: string
|
||||
length: 16
|
||||
slug:
|
||||
type: string
|
||||
length: 128
|
||||
gedmo:
|
||||
slug:
|
||||
separator: _
|
||||
style: camel
|
||||
fields:
|
||||
- title
|
||||
- code
|
||||
indexes:
|
||||
search_idx:
|
||||
columns: slug
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
**Note:** xml driver is not yet adapted for single slug mapping
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
<entity name="Entity\Article" table="sluggables">
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="title" type="string" length="128"/>
|
||||
<field name="code" type="string" length="16"/>
|
||||
<field name="ean" type="string" length="13"/>
|
||||
<field name="slug" type="string" length="156" unique="true">
|
||||
<gedmo:slug unique="true" style="camel" updatable="false" separator="_" fields="title,code,ean" />
|
||||
</field>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="basic-examples"></a>
|
||||
|
||||
## Basic usage examples:
|
||||
|
||||
### To save **Article** and generate slug simply use:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Article();
|
||||
$article->setTitle('the title');
|
||||
$article->setCode('my code');
|
||||
$this->em->persist($article);
|
||||
$this->em->flush();
|
||||
|
||||
echo $article->getSlug();
|
||||
// prints: the-title-my-code
|
||||
```
|
||||
|
||||
### Some other configuration options for **slug** annotation:
|
||||
|
||||
- **fields** (required, default=[]) - list of fields for slug
|
||||
- **updatable** (optional, default=true) - **true** to update the slug on sluggable field changes, **false** - otherwise
|
||||
- **unique** (optional, default=true) - **true** if slug should be unique and if identical it will be prefixed, **false** - otherwise
|
||||
- **unique_base** (optional, default=null) - used in conjunction with **unique**. The name of the entity property that should be used as a key when doing a uniqueness check.
|
||||
- **separator** (optional, default="-") - separator which will separate words in slug
|
||||
- **prefix** (optional, default="") - prefix which will be added to the generated slug
|
||||
- **suffix** (optional, default="") - suffix which will be added to the generated slug
|
||||
- **style** (optional, default="default") - **"default"** all letters will be lowercase, **"camel"** - first word letter will be uppercase, **"upper"**- all word letter will be uppercase and **"lower"**- all word letter will be lowercase
|
||||
- **handlers** (optional, default=[]) - list of slug handlers, like tree path slug, or customized, for example see bellow
|
||||
|
||||
**Note**: handlers are totally optional
|
||||
|
||||
**TreeSlugHandler**
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @Gedmo\Mapping\Annotation\Slug(handlers={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandler(class="Gedmo\Sluggable\Handler\TreeSlugHandler", options={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="parentRelationField", value="parent"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="separator", value="/")
|
||||
* })
|
||||
* }, fields={"title", "code"})
|
||||
* @Doctrine\ORM\Mapping\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
```
|
||||
|
||||
**RelativeSlugHandler**:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* Person domain object class
|
||||
*
|
||||
* @Gedmo\Mapping\Annotation\Slug(handlers={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandler(class="Gedmo\Sluggable\Handler\RelativeSlugHandler", options={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="relationField", value="category"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="relationSlugField", value="slug"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="separator", value="/")
|
||||
* })
|
||||
* }, fields={"title", "code"})
|
||||
* @Doctrine\ORM\Mapping\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
```
|
||||
|
||||
If the relationSlugField you are using is not a slug field but a string field for example you can make
|
||||
sure the relationSlugField is also urilized with:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* Person domain object class
|
||||
*
|
||||
* @Gedmo\Mapping\Annotation\Slug(handlers={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandler(class="Gedmo\Sluggable\Handler\RelativeSlugHandler", options={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="relationField", value="category"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="relationSlugField", value="title"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="separator", value="/"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="urilize", value=true)
|
||||
* })
|
||||
* }, fields={"title", "code"})
|
||||
* @Doctrine\ORM\Mapping\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
```
|
||||
|
||||
This will make sure that the 'title' field in the category entity is url friendly.
|
||||
|
||||
**Note:** if you used **RelativeSlugHandler** - relation object should use in order to sync changes:
|
||||
|
||||
**InversedRelativeSlugHandler**
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* Category domain object class
|
||||
*
|
||||
* @Gedmo\Mapping\Annotation\Slug(handlers={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandler(class="Gedmo\Sluggable\Handler\InversedRelativeSlugHandler", options={
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="relationClass", value="App\Entity\Person"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="mappedBy", value="category"),
|
||||
* @Gedmo\Mapping\Annotation\SlugHandlerOption(name="inverseSlugField", value="slug")
|
||||
* })
|
||||
* }, fields={"title"})
|
||||
* @Doctrine\ORM\Mapping\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
``` php
|
||||
<?php
|
||||
class Article
|
||||
{
|
||||
// ...
|
||||
/**
|
||||
* @Gedmo\Slug(fields={"title", "created"}, style="camel", separator="_", updatable=false, unique=false, dateFormat="d/m/Y H-i-s")
|
||||
* @Doctrine\ORM\Mapping\Column(length=128, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
// ...
|
||||
|
||||
// ...
|
||||
/**
|
||||
* @Doctrine\ORM\Mapping\Column(type="datetime", name="created_at")
|
||||
*/
|
||||
private $createdAt;
|
||||
|
||||
// ...
|
||||
/**
|
||||
* @Doctrine\ORM\Mapping\Column(length=128)
|
||||
*/
|
||||
private $title;
|
||||
// ...
|
||||
public function __construct()
|
||||
{
|
||||
$this->createdAt = new \DateTime;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And now test the result:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Article();
|
||||
$article->setTitle('the title');
|
||||
$article->setCode('my code');
|
||||
$this->em->persist($article);
|
||||
$this->em->flush();
|
||||
|
||||
echo $article->getSlug();
|
||||
// prints: The_Title_My_Code
|
||||
```
|
||||
|
||||
<a name="transliterator"></a>
|
||||
|
||||
## Custom transliterator
|
||||
|
||||
To set your own custom transliterator, which would be used to generate the slug, use:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
$callable = array('My\Class', 'transliterationMethod');
|
||||
$sluggableListener->setTransliterator($callable);
|
||||
|
||||
// or use a closure
|
||||
|
||||
$callable = function($text, $separatorUsed, $objectBeingSlugged) {
|
||||
// ...
|
||||
return $transliteratedText;
|
||||
};
|
||||
$sluggableListener->setTransliterator($callable);
|
||||
```
|
||||
|
||||
<a name="advanced-examples"></a>
|
||||
|
||||
## Advanced examples:
|
||||
|
||||
### Regenerating slug
|
||||
|
||||
In case if you want the slug to regenerate itself based on sluggable fields, set the slug to **null**.
|
||||
|
||||
*Note: in previous versions empty strings would also cause the slug to be regenerated. This behaviour was changed in v2.3.8.*
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$entity = $em->find('Entity\Something', $id);
|
||||
$entity->setSlug(null);
|
||||
|
||||
$em->persist($entity);
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
### Setting the slug manually
|
||||
|
||||
Sometimes you might need to set it manually, etc if generated one does not look satisfying enough.
|
||||
Sluggable will ensure uniqueness of the slug.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$entity = new SomeEntity;
|
||||
$entity->setSluggableField('won\'t be taken into account');
|
||||
$entity->setSlug('the required slug, set manually');
|
||||
|
||||
$em->persist($entity);
|
||||
$em->flush();
|
||||
|
||||
echo $entity->getSlug(); // outputs: "the-required-slug-set-manually"
|
||||
```
|
||||
|
||||
### Using TranslatableListener to translate our slug
|
||||
|
||||
If you want to attach **TranslatableListener** also add it to EventManager after
|
||||
the **SluggableListener**. It is important because slug must be generated first
|
||||
before the creation of it`s translation.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$evm = new \Doctrine\Common\EventManager();
|
||||
$sluggableListener = new \Gedmo\Sluggable\SluggableListener();
|
||||
$evm->addEventSubscriber($sluggableListener);
|
||||
$translatableListener = new \Gedmo\Translatable\TranslatableListener();
|
||||
$translatableListener->setTranslatableLocale('en_us');
|
||||
$evm->addEventSubscriber($translatableListener);
|
||||
// now this event manager should be passed to entity manager constructor
|
||||
```
|
||||
|
||||
And the Entity should look like:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="articles")
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(length=16)
|
||||
*/
|
||||
private $code;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @Gedmo\Slug(fields={"title", "code"})
|
||||
* @ORM\Column(length=128, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=64)
|
||||
*/
|
||||
private $uniqueTitle;
|
||||
|
||||
/**
|
||||
* @Gedmo\Slug(fields={"uniqueTitle"}, prefix="some-prefix-")
|
||||
* @ORM\Column(type="string", length=128, unique=true)
|
||||
*/
|
||||
private $uniqueSlug;
|
||||
|
||||
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
}
|
||||
|
||||
public function getCode()
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function getSlug()
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function getUniqueSlug()
|
||||
{
|
||||
return $this->uniqueSlug;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now the generated slug will be translated by Translatable behavior
|
||||
|
||||
<a name="slug-handlers"></a>
|
||||
|
||||
## Using slug handlers:
|
||||
|
||||
There are built-in slug handlers like described in configuration options of slug, but there
|
||||
can be also customized slug handlers depending on use cases. Usually the most logic use case
|
||||
is for related slug. For instance if user has a **ManyToOne relation to a **Company** we
|
||||
would like to have a url like **http://example.com/knplabs/gedi where **KnpLabs**
|
||||
is a company and user name is **Gedi**. In this case relation has a path separator **/**
|
||||
|
||||
User entity example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Sluggable\Fixture\Handler;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $username;
|
||||
|
||||
/**
|
||||
* @Gedmo\Slug(handlers={
|
||||
* @Gedmo\SlugHandler(class="Gedmo\Sluggable\Handler\RelativeSlugHandler", options={
|
||||
* @Gedmo\SlugHandlerOption(name="relationField", value="company"),
|
||||
* @Gedmo\SlugHandlerOption(name="relationSlugField", value="alias"),
|
||||
* @Gedmo\SlugHandlerOption(name="separator", value="/")
|
||||
* })
|
||||
* }, separator="-", updatable=true, fields={"username"})
|
||||
* @ORM\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Company")
|
||||
*/
|
||||
private $company;
|
||||
|
||||
public function setCompany(Company $company = null)
|
||||
{
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
public function getCompany()
|
||||
{
|
||||
return $this->company;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getSlug()
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Company entity example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Sluggable\Fixture\Handler;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Company
|
||||
{
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Slug(handlers={
|
||||
* @Gedmo\SlugHandler(class="Gedmo\Sluggable\Handler\InversedRelativeSlugHandler", options={
|
||||
* @Gedmo\SlugHandlerOption(name="relationClass", value="Sluggable\Fixture\Handler\User"),
|
||||
* @Gedmo\SlugHandlerOption(name="mappedBy", value="company"),
|
||||
* @Gedmo\SlugHandlerOption(name="inverseSlugField", value="slug")
|
||||
* })
|
||||
* }, fields={"title"})
|
||||
* @ORM\Column(length=64, unique=true)
|
||||
*/
|
||||
private $alias;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getAlias()
|
||||
{
|
||||
return $this->alias;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For other mapping drivers see
|
||||
[xml](https://github.com/Atlantic18/DoctrineExtensions/blob/master/tests/Gedmo/Mapping/Driver/Xml/Mapping.Fixture.Xml.Sluggable.dcm.xml) or [yaml](https://github.com/Atlantic18/DoctrineExtensions/blob/master/tests/Gedmo/Mapping/Driver/Yaml/Mapping.Fixture.Yaml.Category.dcm.yml) examples from tests
|
||||
|
||||
And the example usage:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$company = new Company;
|
||||
$company->setTitle('KnpLabs');
|
||||
$em->persist($company);
|
||||
|
||||
$gedi = new User;
|
||||
$gedi->setUsername('Gedi');
|
||||
$gedi->setCompany($company);
|
||||
$em->persist($gedi);
|
||||
|
||||
$em->flush();
|
||||
|
||||
echo $gedi->getSlug(); // outputs "knplabs/gedi"
|
||||
|
||||
$company->setTitle('KnpLabs Nantes');
|
||||
$em->persist($company);
|
||||
$em->flush();
|
||||
|
||||
echo $gedi->getSlug(); // outputs "knplabs-nantes/gedi"
|
||||
```
|
||||
|
||||
**Note:** tree slug handler, takes a parent relation to build slug recursively.
|
||||
|
||||
Any suggestions on improvements are very welcome
|
||||
286
vendor/gedmo/doctrine-extensions/doc/softdeleteable.md
vendored
Normal file
286
vendor/gedmo/doctrine-extensions/doc/softdeleteable.md
vendored
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
# SoftDeleteable behavior extension for Doctrine 2
|
||||
|
||||
**SoftDeleteable** behavior allows to "soft delete" objects, filtering them
|
||||
at SELECT time by marking them deleted as with a timestamp, but not explicitly removing them from the database.
|
||||
|
||||
Features:
|
||||
|
||||
- Works with DQL DELETE queries (using a Query Hint).
|
||||
- All SELECT queries will be filtered, not matter from where they are executed (Repositories, DQL SELECT queries, etc).
|
||||
- For now, it works only with the ORM
|
||||
- Can be nested with other behaviors
|
||||
- Annotation, Yaml and Xml mapping support for extensions
|
||||
- Support for 'timeAware' option: When creating an entity set a date of deletion in the future and never worry about cleaning up at expiration time.
|
||||
- Support for 'hardDelete' option: When deleting a second time it allows to disable hard delete.
|
||||
|
||||
Content:
|
||||
|
||||
- [Including](#including-extension) the extension
|
||||
- Entity [example](#entity-mapping)
|
||||
- [Yaml](#yaml-mapping) mapping example
|
||||
- [Xml](#xml-mapping) mapping example
|
||||
- Usage [examples](#usage)
|
||||
- Using [Traits](#traits)
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
With SoftDeleteable there's one more step you need to do. You need to add the filter to your configuration:
|
||||
|
||||
``` php
|
||||
|
||||
$config = new Doctrine\ORM\Configuration;
|
||||
|
||||
// Your configs..
|
||||
|
||||
$config->addFilter('soft-deleteable', 'Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter');
|
||||
```
|
||||
|
||||
And then you can access the filter from your EntityManager to enable or disable it with the following code:
|
||||
|
||||
``` php
|
||||
// This will enable the SoftDeleteable filter, so entities which were "soft-deleted" will not appear
|
||||
// in results
|
||||
// You should adapt the filter name to your configuration (ex: softdeleteable)
|
||||
$em->getFilters()->enable('soft-deleteable');
|
||||
|
||||
// This will disable the SoftDeleteable filter, so entities which were "soft-deleted" will appear in results
|
||||
$em->getFilters()->disable('soft-deleteable');
|
||||
```
|
||||
|
||||
Or from your DocumentManager (ODM):
|
||||
|
||||
``` php
|
||||
// This will enable the SoftDeleteable filter, so entities which were "soft-deleted" will not appear
|
||||
// in results
|
||||
// You should adapt the filter name to your configuration (ex: softdeleteable)
|
||||
$em->getFilterCollection()->enable('soft-deleteable');
|
||||
|
||||
// This will disable the SoftDeleteable filter, so entities which were "soft-deleted" will appear in results
|
||||
$em->getFilterCollection()->disable('soft-deleteable');
|
||||
```
|
||||
|
||||
**NOTE:** by default all filters are disabled, so you must explicitly enable **soft-deleteable** filter in your setup
|
||||
or whenever you need it.
|
||||
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
## SoftDeleteable Entity example:
|
||||
|
||||
### SoftDeleteable annotations:
|
||||
- **@Gedmo\Mapping\Annotation\SoftDeleteable** this class annotation tells if a class is SoftDeleteable. It has a
|
||||
mandatory parameter "fieldName", which is the name of the field to be used to hold the known "deletedAt" field. It
|
||||
must be of any of the date types.
|
||||
|
||||
Available configuration options:
|
||||
- **fieldName** - The name of the field that will be used to determine if the object is removed or not (NULL means
|
||||
it's not removed. A date value means it was removed). NOTE: The field MUST be nullable.
|
||||
|
||||
- **hardDelete** - A boolean to enable or disable hard delete after soft delete has already been done. NOTE: Set to true by default.
|
||||
|
||||
**Note:** that SoftDeleteable interface is not necessary, except in cases where
|
||||
you need to identify entity as being SoftDeleteable. The metadata is loaded only once then
|
||||
cache is activated.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=true)
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="title", type="string")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="deletedAt", type="datetime", nullable=true)
|
||||
*/
|
||||
private $deletedAt;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getDeletedAt()
|
||||
{
|
||||
return $this->deletedAt;
|
||||
}
|
||||
|
||||
public function setDeletedAt($deletedAt)
|
||||
{
|
||||
$this->deletedAt = $deletedAt;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## Yaml mapping example:
|
||||
|
||||
Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
|
||||
|
||||
```
|
||||
---
|
||||
Entity\Article:
|
||||
type: entity
|
||||
table: articles
|
||||
gedmo:
|
||||
soft_deleteable:
|
||||
field_name: deletedAt
|
||||
time_aware: false
|
||||
hard_delete: true
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
title:
|
||||
type: string
|
||||
deletedAt:
|
||||
type: date
|
||||
nullable: true
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
|
||||
<entity name="Mapping\Fixture\Xml\Timestampable" table="timestampables">
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="title" type="string" />
|
||||
|
||||
<field name="deletedAt" type="datetime" nullable="true" />
|
||||
|
||||
<gedmo:soft-deleteable field-name="deletedAt" time-aware="false" hard-delete="true" />
|
||||
</entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="usage"></a>
|
||||
|
||||
## Usage:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Article;
|
||||
$article->setTitle('My Article');
|
||||
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
|
||||
// Now if we remove it, it will set the deletedAt field to the actual date
|
||||
$em->remove($article);
|
||||
$em->flush();
|
||||
|
||||
$repo = $em->getRepository('Article');
|
||||
$art = $repo->findOneBy(array('title' => 'My Article'));
|
||||
|
||||
// It should NOT return the article now
|
||||
$this->assertNull($art);
|
||||
|
||||
// But if we disable the filter, the article should appear now
|
||||
$em->getFilters()->disable('soft-deleteable');
|
||||
|
||||
$art = $repo->findOneBy(array('title' => 'My Article'));
|
||||
|
||||
$this->assertTrue(is_object($art));
|
||||
|
||||
// Enable / Disable filter filter, for specified entity (default is enabled for all)
|
||||
$filter = $em->getFilters()->enable('soft-deleteable');
|
||||
$filter->disableForEntity('Entity\Article');
|
||||
$filter->enableForEntity('Entity\Article');
|
||||
|
||||
// Undelete the entity by setting the deletedAt field to null
|
||||
$article->setDeletedAt(null);
|
||||
```
|
||||
|
||||
Easy like that, any suggestions on improvements are very welcome.
|
||||
|
||||
<a name="traits"></a>
|
||||
|
||||
## Traits
|
||||
|
||||
You can use softDeleteable traits for quick **deletedAt** timestamp 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
|
||||
<?php
|
||||
namespace SoftDeleteable\Fixture;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false, hardDelete=true)
|
||||
*/
|
||||
class UsingTrait
|
||||
{
|
||||
/**
|
||||
* Hook SoftDeleteable behavior
|
||||
* updates deletedAt field
|
||||
*/
|
||||
use SoftDeleteableEntity;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=128)
|
||||
*/
|
||||
private $title;
|
||||
}
|
||||
```
|
||||
|
||||
Traits are very simple and if you use different field names I recommend to simply create your
|
||||
own ones based per project. These ones are standing as an example.
|
||||
328
vendor/gedmo/doctrine-extensions/doc/sortable.md
vendored
Normal file
328
vendor/gedmo/doctrine-extensions/doc/sortable.md
vendored
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
# Sortable behavior extension for Doctrine2
|
||||
|
||||
**Sortable** behavior will maintain a position field for ordering
|
||||
entities.
|
||||
|
||||
Features:
|
||||
|
||||
- Automatic handling of position index
|
||||
- Group entity ordering by one or more fields
|
||||
- Can be nested with other behaviors
|
||||
- Annotation, Yaml and Xml mapping support for extensions
|
||||
|
||||
**Note:**
|
||||
|
||||
- Public [Sortable repository](http://github.com/Atlantic18/DoctrineExtensions "Sortable extension on Github") is available on github
|
||||
- Last update date: **2012-01-02**
|
||||
|
||||
**Portability:**
|
||||
|
||||
- **Sortable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle)
|
||||
ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions
|
||||
|
||||
This article will cover the basic installation and functionality of **Sortable**
|
||||
behavior
|
||||
|
||||
Content:
|
||||
|
||||
- [Including](#including-extension) the extension
|
||||
- Entity [example](#entity-mapping)
|
||||
- [Yaml](#yaml-mapping) mapping example
|
||||
- [Xml](#xml-mapping) mapping example
|
||||
- Basic usage [examples](#basic-examples)
|
||||
- Custom comparison [method](#custom-comparisons)
|
||||
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
## Sortable Entity example:
|
||||
|
||||
### Sortable annotations:
|
||||
|
||||
- **@Gedmo\Mapping\Annotation\SortableGroup** it will use this field for **grouping**
|
||||
- **@Gedmo\Mapping\Annotation\SortablePosition** it will use this column to store **position** index
|
||||
|
||||
**Note:** that Sortable interface is not necessary, except in cases there
|
||||
you need to identify entity as being Sortable. The metadata is loaded only once then
|
||||
cache is activated
|
||||
|
||||
**Note:** that you should register SortableRepository (or a subclass) as the repository in the Entity
|
||||
annotation to benefit from its query methods.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="items")
|
||||
* @ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
|
||||
*/
|
||||
class Item
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="name", type="string", length=64)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @Gedmo\SortablePosition
|
||||
* @ORM\Column(name="position", type="integer")
|
||||
*/
|
||||
private $position;
|
||||
|
||||
/**
|
||||
* @Gedmo\SortableGroup
|
||||
* @ORM\Column(name="category", type="string", length=128)
|
||||
*/
|
||||
private $category;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setPosition($position)
|
||||
{
|
||||
$this->position = $position;
|
||||
}
|
||||
|
||||
public function getPosition()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function setCategory($category)
|
||||
{
|
||||
$this->category = $category;
|
||||
}
|
||||
|
||||
public function getCategory()
|
||||
{
|
||||
return $this->category;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## Yaml mapping example
|
||||
|
||||
Yaml mapped Item: **/mapping/yaml/Entity.Item.dcm.yml**
|
||||
|
||||
```
|
||||
---
|
||||
Entity\Item:
|
||||
type: entity
|
||||
table: items
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
name:
|
||||
type: string
|
||||
length: 64
|
||||
position:
|
||||
type: integer
|
||||
gedmo:
|
||||
- sortablePosition
|
||||
category:
|
||||
type: string
|
||||
length: 128
|
||||
gedmo:
|
||||
- sortableGroup
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
<entity name="Entity\Item" table="items">
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="name" type="string" length="128">
|
||||
</field>
|
||||
|
||||
<field name="position" type="integer">
|
||||
<gedmo:sortable-position/>
|
||||
</field>
|
||||
<field name="category" type="string" length="128">
|
||||
<gedmo:sortable-group />
|
||||
</field>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="basic-examples"></a>
|
||||
|
||||
## Basic usage examples:
|
||||
|
||||
### To save **Items** at the end of the sorting list simply do:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// By default, items are appended to the sorting list
|
||||
$item1 = new Item();
|
||||
$item1->setName('item 1');
|
||||
$item1->setCategory('category 1');
|
||||
$this->em->persist($item1);
|
||||
|
||||
$item2 = new Item();
|
||||
$item2->setName('item 2');
|
||||
$item2->setCategory('category 1');
|
||||
$this->em->persist($item2);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
echo $item1->getPosition();
|
||||
// prints: 0
|
||||
echo $item2->getPosition();
|
||||
// prints: 1
|
||||
```
|
||||
|
||||
### Save **Item** at a given position:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$item1 = new Item();
|
||||
$item1->setName('item 1');
|
||||
$item1->setCategory('category 1');
|
||||
$this->em->persist($item1);
|
||||
|
||||
$item2 = new Item();
|
||||
$item2->setName('item 2');
|
||||
$item2->setCategory('category 1');
|
||||
$this->em->persist($item2);
|
||||
|
||||
$item0 = new Item();
|
||||
$item0->setName('item 0');
|
||||
$item0->setCategory('category 1');
|
||||
$item0->setPosition(0);
|
||||
$this->em->persist($item0);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
$repo = $this->em->getRepository('Entity\\Item');
|
||||
$items = $repo->getBySortableGroupsQuery(array('category' => 'category 1'))->getResult();
|
||||
foreach ($items as $item) {
|
||||
echo "{$item->getPosition()}: {$item->getName()}\n";
|
||||
}
|
||||
// prints:
|
||||
// 0: item 0
|
||||
// 1: item 1
|
||||
// 2: item 2
|
||||
```
|
||||
|
||||
### Reordering the sorted list:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$item1 = new Item();
|
||||
$item1->setName('item 1');
|
||||
$item1->setCategory('category 1');
|
||||
$this->em->persist($item1);
|
||||
|
||||
$item2 = new Item();
|
||||
$item2->setName('item 2');
|
||||
$item2->setCategory('category 1');
|
||||
$this->em->persist($item2);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
// Update the position of item2
|
||||
$item2->setPosition(0);
|
||||
$this->em->persist($item2);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
$repo = $this->em->getRepository('Entity\\Item');
|
||||
$items = $repo->getBySortableGroupsQuery(array('category' => 'category 1'))->getResult();
|
||||
foreach ($items as $item) {
|
||||
echo "{$item->getPosition()}: {$item->getName()}\n";
|
||||
}
|
||||
// prints:
|
||||
// 0: item 2
|
||||
// 1: item 1
|
||||
|
||||
```
|
||||
|
||||
### Using a foreign_key / relation as SortableGroup
|
||||
|
||||
If you want to use a foreign key / relation as sortable group, you have to put @Gedmo\SortableGroup annotation on ManyToOne annotation:
|
||||
|
||||
```
|
||||
/**
|
||||
* @Gedmo\SortableGroup
|
||||
* @ORM\ManyToOne(targetEntity="Item", inversedBy="children")
|
||||
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="SET NULL")
|
||||
*/
|
||||
private $parent;
|
||||
```
|
||||
|
||||
|
||||
To move an item at the end of the list, you can set the position to `-1`:
|
||||
|
||||
```
|
||||
$item2->setPosition(-1);
|
||||
```
|
||||
|
||||
<a name="custom-comparisons"></a>
|
||||
|
||||
## Custom comparison:
|
||||
|
||||
Sortable works by comparing objects in the same group to see how they should be positioned. From time to time you may want to customize the way these
|
||||
objects are compared by simply implementing the Doctrine\Common\Comparable interface
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\Common\Comparable;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="items")
|
||||
* @ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
|
||||
*/
|
||||
class Item implements Comparable
|
||||
{
|
||||
public function compareTo($other)
|
||||
{
|
||||
// return 1 if this object is considered greater than the compare value
|
||||
|
||||
// return -1 if this object is considered less than the compare value
|
||||
|
||||
// return 0 if this object is considered equal to the compare value
|
||||
}
|
||||
}
|
||||
```
|
||||
500
vendor/gedmo/doctrine-extensions/doc/symfony2.md
vendored
Normal file
500
vendor/gedmo/doctrine-extensions/doc/symfony2.md
vendored
Normal file
|
|
@ -0,0 +1,500 @@
|
|||
# Install Gedmo Doctrine2 extensions in Symfony2
|
||||
|
||||
Configure full featured [Doctrine2 extensions](http://github.com/Atlantic18/DoctrineExtensions) for your symfony2 project.
|
||||
This post will show you - how to create a simple configuration file to manage extensions with
|
||||
ability to use all features it provides.
|
||||
Interested? then bear with me! and don't be afraid, we're not diving into security component :)
|
||||
|
||||
This post will put some light over the shed of extension installation and mapping configuration
|
||||
of Doctrine2. It does not require any additional dependencies and gives you full power
|
||||
over management of extensions.
|
||||
|
||||
Content:
|
||||
|
||||
- [Symfony2](#sf2-app) application
|
||||
- Extensions metadata [mapping](#ext-mapping)
|
||||
- Extension [listeners](#ext-listeners)
|
||||
- Usage [example](#ext-example)
|
||||
- Some [tips](#more-tips)
|
||||
- [Alternative](#alternative) over configuration
|
||||
|
||||
<a name="sf2-app"></a>
|
||||
|
||||
## Symfony2 application
|
||||
|
||||
First of all, we will need a symfony2 startup application, let's say [symfony-standard edition
|
||||
with composer](http://github.com/KnpLabs/symfony-with-composer). Follow the standard setup:
|
||||
|
||||
- `git clone git://github.com/KnpLabs/symfony-with-composer.git example`
|
||||
- `cd example && rm -rf .git && php bin/vendors install`
|
||||
- ensure your application loads and meets requirements, by following the url: **http://your_virtual_host/app_dev.php**
|
||||
|
||||
Now let's add the **gedmo/doctrine-extensions** into **composer.json**
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"symfony/symfony": ">=2.0.9,<2.1.0-dev",
|
||||
"doctrine/orm": ">=2.1.0,<2.2.0-dev",
|
||||
"twig/extensions": "*",
|
||||
|
||||
"symfony/assetic-bundle": "*",
|
||||
"sensio/generator-bundle": "2.0.*",
|
||||
"sensio/framework-extra-bundle": "2.0.*",
|
||||
"sensio/distribution-bundle": "2.0.*",
|
||||
"jms/security-extra-bundle": "1.0.*",
|
||||
"gedmo/doctrine-extensions": "dev-master"
|
||||
},
|
||||
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Acme": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Update vendors, run: **php composer.phar update gedmo/doctrine-extensions**
|
||||
Initially in this package you have **doctrine2 orm** included, so we will base our setup
|
||||
and configuration for this specific connection. Do not forget to configure your database
|
||||
connection parameters, edit **app/config/parameters.yml**
|
||||
|
||||
<a name="ext-mapping"></a>
|
||||
|
||||
## Mapping
|
||||
|
||||
Let's start from the mapping. In case you use the **translatable**, **tree** or **loggable**
|
||||
extension you will need to map those abstract mapped superclasses for your ORM to be aware of.
|
||||
To do so, add some mapping info to your **doctrine.orm** configuration, edit **app/config/config.yml**:
|
||||
|
||||
```yaml
|
||||
doctrine:
|
||||
dbal:
|
||||
# your dbal config here
|
||||
|
||||
orm:
|
||||
auto_generate_proxy_classes: %kernel.debug%
|
||||
auto_mapping: true
|
||||
# only these lines are added additionally
|
||||
mappings:
|
||||
translatable:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Translatable\Entity
|
||||
# make sure vendor library location is correct
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
|
||||
```
|
||||
|
||||
After that, running **php app/console doctrine:mapping:info** you should see the output:
|
||||
|
||||
```
|
||||
Found 3 entities mapped in entity manager default:
|
||||
[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation
|
||||
[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation
|
||||
[OK] Gedmo\Translatable\Entity\Translation
|
||||
```
|
||||
Well, we mapped only **translatable** for now, it really depends on your needs, which extensions
|
||||
your application uses.
|
||||
|
||||
**Note:** there is **Gedmo\Translatable\Entity\Translation** which is not a super class, in that case
|
||||
if you create a doctrine schema, it will add **ext_translations** table, which might not be useful
|
||||
to you also. To skip mapping of these entities, you can map **only superclasses**
|
||||
|
||||
```yaml
|
||||
mappings:
|
||||
translatable:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Translatable\Entity
|
||||
# make sure vendor library location is correct
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass"
|
||||
```
|
||||
|
||||
The configuration above, adds a **/MappedSuperclass** into directory depth, after running
|
||||
**php app/console doctrine:mapping:info** you should only see now:
|
||||
|
||||
```
|
||||
Found 2 entities mapped in entity manager default:
|
||||
[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation
|
||||
[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation
|
||||
```
|
||||
|
||||
This is very useful for advanced requirements and quite simple to understand. So now let's map
|
||||
everything the extensions provide:
|
||||
|
||||
```yaml
|
||||
# only orm config branch of doctrine
|
||||
orm:
|
||||
auto_generate_proxy_classes: %kernel.debug%
|
||||
auto_mapping: true
|
||||
# only these lines are added additionally
|
||||
mappings:
|
||||
translatable:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Translatable\Entity
|
||||
# make sure vendor library location is correct
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
|
||||
loggable:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Loggable\Entity
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity"
|
||||
tree:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Tree\Entity
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"
|
||||
```
|
||||
|
||||
<a name="ext-listeners"></a>
|
||||
|
||||
## Doctrine extension listener services
|
||||
|
||||
Next, the heart of extensions are behavioral listeners which pours all the sugar. We will
|
||||
create a **yml** service file in our config directory. The setup can be different, your config could be located
|
||||
in the bundle, it depends on your preferences. Edit **app/config/doctrine_extensions.yml**
|
||||
|
||||
```yaml
|
||||
# services to handle doctrine extensions
|
||||
# import it in config.yml
|
||||
services:
|
||||
# KernelRequest listener
|
||||
extension.listener:
|
||||
class: Acme\DemoBundle\Listener\DoctrineExtensionListener
|
||||
calls:
|
||||
- [ setContainer, [ "@service_container" ] ]
|
||||
tags:
|
||||
# translatable sets locale after router processing
|
||||
- { name: kernel.event_listener, event: kernel.request, method: onLateKernelRequest, priority: -10 }
|
||||
# loggable hooks user username if one is in security context
|
||||
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
|
||||
# translatable sets locale such as default application locale before command execute
|
||||
- { name: kernel.event_listener, event: console.command, method: onConsoleCommand, priority: -10 }
|
||||
|
||||
|
||||
# Doctrine Extension listeners to handle behaviors
|
||||
gedmo.listener.tree:
|
||||
class: Gedmo\Tree\TreeListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
gedmo.listener.translatable:
|
||||
class: Gedmo\Translatable\TranslatableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
- [ setDefaultLocale, [ %locale% ] ]
|
||||
- [ setTranslationFallback, [ false ] ]
|
||||
|
||||
gedmo.listener.timestampable:
|
||||
class: Gedmo\Timestampable\TimestampableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
gedmo.listener.sluggable:
|
||||
class: Gedmo\Sluggable\SluggableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
gedmo.listener.sortable:
|
||||
class: Gedmo\Sortable\SortableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
gedmo.listener.loggable:
|
||||
class: Gedmo\Loggable\LoggableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
gedmo.listener.blameable:
|
||||
class: Gedmo\Blameable\BlameableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
```
|
||||
|
||||
So what does it include in general? Well, it creates services for all extension listeners.
|
||||
You can remove some which you do not use, or change them as you need. **Translatable** for instance,
|
||||
sets the default locale to the value of your `%locale%` parameter, you can configure it differently.
|
||||
|
||||
**Note:** In case you noticed, there is **Acme\DemoBundle\Listener\DoctrineExtensionListener**.
|
||||
You will need to create this listener class if you use **loggable** or **translatable**
|
||||
behaviors. This listener will set the **locale used** from request and **username** to
|
||||
loggable. So, to finish the setup create **Acme\DemoBundle\Listener\DoctrineExtensionListener**
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
// file: src/Acme/DemoBundle/Listener/DoctrineExtensionListener.php
|
||||
|
||||
namespace Acme\DemoBundle\Listener;
|
||||
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpKernel\Kernel;
|
||||
|
||||
class DoctrineExtensionListener implements ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
public function setContainer(ContainerInterface $container = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function onLateKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
$translatable = $this->container->get('gedmo.listener.translatable');
|
||||
$translatable->setTranslatableLocale($event->getRequest()->getLocale());
|
||||
}
|
||||
|
||||
public function onConsoleCommand()
|
||||
{
|
||||
$this->container->get('gedmo.listener.translatable')
|
||||
->setTranslatableLocale($this->container->get('translator')->getLocale());
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
if (Kernel::MAJOR_VERSION == 2 && Kernel::MINOR_VERSION < 6) {
|
||||
$securityContext = $this->container->get('security.context', ContainerInterface::NULL_ON_INVALID_REFERENCE);
|
||||
if (null !== $securityContext && null !== $securityContext->getToken() && $securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
|
||||
# for loggable behavior
|
||||
$loggable = $this->container->get('gedmo.listener.loggable');
|
||||
$loggable->setUsername($securityContext->getToken()->getUsername());
|
||||
|
||||
# for blameable behavior
|
||||
$blameable = $this->container->get('gedmo.listener.blameable');
|
||||
$blameable->setUserValue($securityContext->getToken()->getUser());
|
||||
}
|
||||
}
|
||||
else {
|
||||
$tokenStorage = $this->container->get('security.token_storage')->getToken();
|
||||
$authorizationChecker = $this->container->get('security.authorization_checker');
|
||||
if (null !== $tokenStorage && $authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
|
||||
# for loggable behavior
|
||||
$loggable = $this->container->get('gedmo.listener.loggable');
|
||||
$loggable->setUsername($tokenStorage->getUser());
|
||||
|
||||
# for blameable behavior
|
||||
$blameable = $this->container->get('gedmo.listener.blameable');
|
||||
$blameable->setUserValue($tokenStorage->getUser());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
Do not forget to import **doctrine_extensions.yml** in your **app/config/config.yml**:
|
||||
|
||||
```yaml
|
||||
# file: app/config/config.yml
|
||||
imports:
|
||||
- { resource: parameters.yml }
|
||||
- { resource: security.yml }
|
||||
- { resource: doctrine_extensions.yml }
|
||||
|
||||
# ... configuration follows
|
||||
```
|
||||
|
||||
<a name="ext-example"></a>
|
||||
|
||||
## Example
|
||||
|
||||
After that, you have your extensions set up and ready to be used! Too easy right? Well,
|
||||
if you do not believe me, let's create a simple entity in our **Acme** project:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
// file: src/Acme/DemoBundle/Entity/BlogPost.php
|
||||
|
||||
namespace Acme\DemoBundle\Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo; // gedmo annotations
|
||||
use Doctrine\ORM\Mapping as ORM; // doctrine orm annotations
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class BlogPost
|
||||
{
|
||||
/**
|
||||
* @Gedmo\Slug(fields={"title"}, updatable=false, separator="_")
|
||||
* @ORM\Id
|
||||
* @ORM\Column(length=32, unique=true)
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
* @ORM\Column(name="created", type="datetime")
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="updated", type="datetime")
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
*/
|
||||
private $updated;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getCreated()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function getUpdated()
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's have some fun:
|
||||
|
||||
- if you have not created the database yet, run `php app/console doctrine:database:create`
|
||||
- create the schema `php app/console doctrine:schema:create`
|
||||
|
||||
Everything will work just fine, you can modify the **Acme\DemoBundle\Controller\DemoController**
|
||||
and add an action to test how it works:
|
||||
|
||||
```php
|
||||
// file: src/Acme/DemoBundle/Controller/DemoController.php
|
||||
// include this code portion
|
||||
|
||||
/**
|
||||
* @Route("/posts", name="_demo_posts")
|
||||
*/
|
||||
public function postsAction()
|
||||
{
|
||||
$em = $this->getDoctrine()->getEntityManager();
|
||||
$repository = $em->getRepository('AcmeDemoBundle:BlogPost');
|
||||
// create some posts in case if there aren't any
|
||||
if (!$repository->findOneById('hello_world')) {
|
||||
$post = new \Acme\DemoBundle\Entity\BlogPost();
|
||||
$post->setTitle('Hello world');
|
||||
|
||||
$next = new \Acme\DemoBundle\Entity\BlogPost();
|
||||
$next->setTitle('Doctrine extensions');
|
||||
|
||||
$em->persist($post);
|
||||
$em->persist($next);
|
||||
$em->flush();
|
||||
}
|
||||
$posts = $em
|
||||
->createQuery('SELECT p FROM AcmeDemoBundle:BlogPost p')
|
||||
->getArrayResult()
|
||||
;
|
||||
die(var_dump($posts));
|
||||
}
|
||||
```
|
||||
|
||||
Now if you follow the url: **http://your_virtual_host/app_dev.php/demo/posts** you
|
||||
should see a print of posts, this is only an extension demo, we will not create a template.
|
||||
|
||||
<a name="more-tips"></a>
|
||||
|
||||
## More tips
|
||||
|
||||
Regarding, the setup, I do not think it's too complicated to use, in general it is simple
|
||||
enough, and lets you understand at least small parts on how you can hook mappings into doctrine, and
|
||||
how easily extension services are added. This configuration does not hide anything behind
|
||||
curtains and allows you to modify the configuration as you require.
|
||||
|
||||
### Multiple entity managers
|
||||
|
||||
If you use more than one entity manager, you can simply tag the listener
|
||||
with other the manager name:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
# tree behavior
|
||||
gedmo.listener.tree:
|
||||
class: Gedmo\Tree\TreeListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
# additional ORM subscriber
|
||||
- { name: doctrine.event_subscriber, connection: other_connection }
|
||||
# ODM MongoDb subscriber, where **default** is manager name
|
||||
- { name: doctrine_mongodb.odm.event_subscriber }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ @annotation_reader ] ]
|
||||
```
|
||||
|
||||
Regarding, mapping of ODM mongodb, it's basically the same:
|
||||
|
||||
```yaml
|
||||
doctrine_mongodb:
|
||||
default_database: 'my_database'
|
||||
default_connection: 'default'
|
||||
default_document_manager: 'default'
|
||||
connections:
|
||||
default: ~
|
||||
document_managers:
|
||||
default:
|
||||
connection: 'default'
|
||||
auto_mapping: true
|
||||
mappings:
|
||||
translatable:
|
||||
type: annotation
|
||||
alias: GedmoDocument
|
||||
prefix: Gedmo\Translatable\Document
|
||||
# make sure vendor library location is correct
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document"
|
||||
```
|
||||
|
||||
This also shows, how to make mappings based on single manager. All what differs is that **Document**
|
||||
instead of **Entity** is used. I haven't tested it with mongo though.
|
||||
|
||||
**Note:** [extension repository](http://github.com/Atlantic18/DoctrineExtensions) contains all
|
||||
[documentation](http://github.com/Atlantic18/DoctrineExtensions/tree/master/doc) you may need
|
||||
to understand how you can use it in your projects.
|
||||
|
||||
<a name="alternative"></a>
|
||||
|
||||
## Alternative over configuration
|
||||
|
||||
You can use [StofDoctrineExtensionsBundle](http://github.com/stof/StofDoctrineExtensionsBundle) which is a wrapper of these extensions
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Make sure there are no *.orm.yml or *.orm.xml files for your Entities in your bundles Resources/config/doctrine directory. With those files in place the annotations won't be taken into account.
|
||||
434
vendor/gedmo/doctrine-extensions/doc/symfony4.md
vendored
Normal file
434
vendor/gedmo/doctrine-extensions/doc/symfony4.md
vendored
Normal file
|
|
@ -0,0 +1,434 @@
|
|||
# Install Gedmo Doctrine2 extensions in Symfony 4
|
||||
|
||||
Configure full featured [Doctrine2 extensions](http://github.com/Atlantic18/DoctrineExtensions) for your symfony 4 project.
|
||||
This post will show you - how to create a simple configuration file to manage extensions with
|
||||
ability to use all features it provides.
|
||||
Interested? then bear with me! and don't be afraid, we're not diving into security component :)
|
||||
|
||||
This post will put some light over the shed of extension installation and mapping configuration
|
||||
of Doctrine2. It does not require any additional dependencies and gives you full power
|
||||
over management of extensions.
|
||||
|
||||
Content:
|
||||
|
||||
- [Symfony 4](#sf4-app) application
|
||||
- Extensions metadata [mapping](#ext-mapping)
|
||||
- Extension [listeners](#ext-listeners)
|
||||
- Usage [example](#ext-example)
|
||||
- Some [tips](#more-tips)
|
||||
- [Alternative](#alternative) over configuration
|
||||
|
||||
<a name="sf4-app"></a>
|
||||
|
||||
## Symfony 4 application
|
||||
|
||||
First of all, we will need a symfony 4 startup application, let's say [symfony-standard edition
|
||||
with composer](https://symfony.com/doc/current/best_practices/creating-the-project.html)
|
||||
|
||||
- `composer create-project symfony/skeleton [project name]`
|
||||
|
||||
Now let's add the **gedmo/doctrine-extensions**
|
||||
|
||||
You can find the doctrine-extensions project on packagist: https://packagist.org/packages/gedmo/doctrine-extensions
|
||||
|
||||
To add it to your project:
|
||||
- `composer require gedmo/doctrine-extensions`
|
||||
|
||||
<a name="ext-mapping"></a>
|
||||
|
||||
## Mapping
|
||||
|
||||
Let's start from the mapping. In case you use the **translatable**, **tree** or **loggable**
|
||||
extension you will need to map those abstract mapped superclasses for your ORM to be aware of.
|
||||
To do so, add some mapping info to your **doctrine.orm** configuration, edit **app/config/config.yml**:
|
||||
|
||||
```yaml
|
||||
doctrine:
|
||||
dbal:
|
||||
# your dbal config here
|
||||
|
||||
orm:
|
||||
auto_generate_proxy_classes: %kernel.debug%
|
||||
auto_mapping: true
|
||||
# only these lines are added additionally
|
||||
mappings:
|
||||
translatable:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Translatable\Entity
|
||||
# make sure vendor library location is correct
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
|
||||
```
|
||||
|
||||
After that, running **php bin/console doctrine:mapping:info** you should see the output:
|
||||
|
||||
```
|
||||
Found 3 entities mapped in entity manager default:
|
||||
[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation
|
||||
[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation
|
||||
[OK] Gedmo\Translatable\Entity\Translation
|
||||
```
|
||||
Well, we mapped only **translatable** for now, it really depends on your needs, which extensions
|
||||
your application uses.
|
||||
|
||||
**Note:** there is **Gedmo\Translatable\Entity\Translation** which is not a super class, in that case
|
||||
if you create a doctrine schema, it will add **ext_translations** table, which might not be useful
|
||||
to you also. To skip mapping of these entities, you can map **only superclasses**
|
||||
|
||||
```yaml
|
||||
mappings:
|
||||
translatable:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Translatable\Entity
|
||||
# make sure vendor library location is correct
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity/MappedSuperclass"
|
||||
```
|
||||
|
||||
The configuration above, adds a **/MappedSuperclass** into directory depth, after running
|
||||
**php bin/console doctrine:mapping:info** you should only see now:
|
||||
|
||||
```
|
||||
Found 2 entities mapped in entity manager default:
|
||||
[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation
|
||||
[OK] Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation
|
||||
```
|
||||
|
||||
This is very useful for advanced requirements and quite simple to understand. So now let's map
|
||||
everything the extensions provide:
|
||||
|
||||
```yaml
|
||||
# only orm config branch of doctrine
|
||||
orm:
|
||||
auto_generate_proxy_classes: %kernel.debug%
|
||||
auto_mapping: true
|
||||
# only these lines are added additionally
|
||||
mappings:
|
||||
translatable:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Translatable\Entity
|
||||
# make sure vendor library location is correct
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity"
|
||||
loggable:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Loggable\Entity
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity"
|
||||
tree:
|
||||
type: annotation
|
||||
alias: Gedmo
|
||||
prefix: Gedmo\Tree\Entity
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Tree/Entity"
|
||||
```
|
||||
|
||||
<a name="ext-listeners"></a>
|
||||
|
||||
## Doctrine extension listener services
|
||||
|
||||
Next, the heart of extensions are behavioral listeners which pours all the sugar. We will
|
||||
create a **yml** service file in our config directory. The setup can be different, your config could be located
|
||||
in the bundle, it depends on your preferences. Edit **app/config/packages/doctrine_extensions.yml**
|
||||
|
||||
```yaml
|
||||
# services to handle doctrine extensions
|
||||
# import it in config.yml
|
||||
services:
|
||||
# Doctrine Extension listeners to handle behaviors
|
||||
gedmo.listener.tree:
|
||||
class: Gedmo\Tree\TreeListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
Gedmo\Translatable\TranslatableListener:
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
- [ setDefaultLocale, [ %locale% ] ]
|
||||
- [ setTranslationFallback, [ false ] ]
|
||||
|
||||
gedmo.listener.timestampable:
|
||||
class: Gedmo\Timestampable\TimestampableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
gedmo.listener.sluggable:
|
||||
class: Gedmo\Sluggable\SluggableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
gedmo.listener.sortable:
|
||||
class: Gedmo\Sortable\SortableListener
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
Gedmo\Loggable\LoggableListener:
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
Gedmo\Blameable\BlameableListener:
|
||||
tags:
|
||||
- { name: doctrine.event_subscriber, connection: default }
|
||||
calls:
|
||||
- [ setAnnotationReader, [ "@annotation_reader" ] ]
|
||||
|
||||
```
|
||||
|
||||
So what does it include in general? Well, it creates services for all extension listeners.
|
||||
You can remove some which you do not use, or change them as you need. **Translatable** for instance,
|
||||
sets the default locale to the value of your `%locale%` parameter, you can configure it differently.
|
||||
|
||||
**Note:** In case you noticed, there is **EventSubscriber\DoctrineExtensionSubscriber**.
|
||||
You will need to create this subscriber class if you use **loggable** , **translatable** or **blameable**
|
||||
behaviors. This listener will set the **locale used** from request and **username** to
|
||||
loggable and blameable. So, to finish the setup create **EventSubscriber\DoctrineExtensionSubscriber**
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
namespace App\EventSubscriber;
|
||||
|
||||
use Gedmo\Blameable\BlameableListener;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||
|
||||
class DoctrineExtensionSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var BlameableListener
|
||||
*/
|
||||
private $blameableListener;
|
||||
/**
|
||||
* @var TokenStorageInterface
|
||||
*/
|
||||
private $tokenStorage;
|
||||
/**
|
||||
* @var TranslatableListener
|
||||
*/
|
||||
private $translatableListener;
|
||||
/**
|
||||
* @var LoggableListener
|
||||
*/
|
||||
private $loggableListener;
|
||||
|
||||
|
||||
public function __construct(
|
||||
BlameableListener $blameableListener,
|
||||
TokenStorageInterface $tokenStorage,
|
||||
TranslatableListener $translatableListener,
|
||||
LoggableListener $loggableListener
|
||||
) {
|
||||
$this->blameableListener = $blameableListener;
|
||||
$this->tokenStorage = $tokenStorage;
|
||||
$this->translatableListener = $translatableListener;
|
||||
$this->loggableListener = $loggableListener;
|
||||
}
|
||||
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
KernelEvents::REQUEST => 'onKernelRequest',
|
||||
KernelEvents::FINISH_REQUEST => 'onLateKernelRequest'
|
||||
];
|
||||
}
|
||||
public function onKernelRequest(): void
|
||||
{
|
||||
if ($this->tokenStorage !== null &&
|
||||
$this->tokenStorage->getToken() !== null &&
|
||||
$this->tokenStorage->getToken()->isAuthenticated() === true
|
||||
) {
|
||||
$this->blameableListener->setUserValue($this->tokenStorage->getToken()->getUser());
|
||||
}
|
||||
}
|
||||
|
||||
public function onLateKernelRequest(FinishRequestEvent $event): void
|
||||
{
|
||||
$this->translatableListener->setTranslatableLocale($event->getRequest()->getLocale());
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
<a name="ext-example"></a>
|
||||
|
||||
## Example
|
||||
|
||||
After that, you have your extensions set up and ready to be used! Too easy right? Well,
|
||||
if you do not believe me, let's create a simple entity in our **Acme** project:
|
||||
|
||||
```php
|
||||
|
||||
<?php
|
||||
|
||||
// file: src/Entity/BlogPost.php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo; // gedmo annotations
|
||||
use Doctrine\ORM\Mapping as ORM; // doctrine orm annotations
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class BlogPost
|
||||
{
|
||||
/**
|
||||
* @Gedmo\Slug(fields={"title"}, updatable=false, separator="_")
|
||||
* @ORM\Id
|
||||
* @ORM\Column(length=32, unique=true)
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
* @ORM\Column(name="created", type="datetime")
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="updated", type="datetime")
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
*/
|
||||
private $updated;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getCreated()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function getUpdated()
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now, let's have some fun:
|
||||
|
||||
- if you have not created the database yet, run `php bin/console doctrine:database:create`
|
||||
- create the schema `php bin/console doctrine:schema:create`
|
||||
|
||||
Everything will work just fine, you can modify the **App\Controller\DemoController**
|
||||
and add an action to test how it works:
|
||||
|
||||
```php
|
||||
// file: src/Acme/DemoBundle/Controller/DemoController.php
|
||||
// include this code portion
|
||||
|
||||
/**
|
||||
* @Route("/posts", name="_demo_posts")
|
||||
*/
|
||||
public function postsAction()
|
||||
{
|
||||
$em = $this->getDoctrine()->getEntityManager();
|
||||
$repository = $em->getRepository('AcmeDemoBundle:BlogPost');
|
||||
// create some posts in case if there aren't any
|
||||
if (!$repository->findOneById('hello_world')) {
|
||||
$post = new \Acme\DemoBundle\Entity\BlogPost();
|
||||
$post->setTitle('Hello world');
|
||||
|
||||
$next = new \Acme\DemoBundle\Entity\BlogPost();
|
||||
$next->setTitle('Doctrine extensions');
|
||||
|
||||
$em->persist($post);
|
||||
$em->persist($next);
|
||||
$em->flush();
|
||||
}
|
||||
$posts = $em
|
||||
->createQuery('SELECT p FROM AcmeDemoBundle:BlogPost p')
|
||||
->getArrayResult()
|
||||
;
|
||||
die(var_dump($posts));
|
||||
}
|
||||
```
|
||||
|
||||
Now if you follow the url: **http://your_virtual_host/app_dev.php/demo/posts** you
|
||||
should see a print of posts, this is only an extension demo, we will not create a template.
|
||||
|
||||
<a name="more-tips"></a>
|
||||
|
||||
## More tips
|
||||
|
||||
Regarding, the setup, I do not think it's too complicated to use, in general it is simple
|
||||
enough, and lets you understand at least small parts on how you can hook mappings into doctrine, and
|
||||
how easily extension services are added. This configuration does not hide anything behind
|
||||
curtains and allows you to modify the configuration as you require.
|
||||
|
||||
### Multiple entity managers
|
||||
|
||||
If you use more than one entity manager, you can simply tag the subscriber
|
||||
with other the manager name:
|
||||
|
||||
|
||||
Regarding, mapping of ODM mongodb, it's basically the same:
|
||||
|
||||
```yaml
|
||||
doctrine_mongodb:
|
||||
default_database: 'my_database'
|
||||
default_connection: 'default'
|
||||
default_document_manager: 'default'
|
||||
connections:
|
||||
default: ~
|
||||
document_managers:
|
||||
default:
|
||||
connection: 'default'
|
||||
auto_mapping: true
|
||||
mappings:
|
||||
translatable:
|
||||
type: annotation
|
||||
alias: GedmoDocument
|
||||
prefix: Gedmo\Translatable\Document
|
||||
# make sure vendor library location is correct
|
||||
dir: "%kernel.root_dir%/../vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Document"
|
||||
```
|
||||
|
||||
This also shows, how to make mappings based on single manager. All what differs is that **Document**
|
||||
instead of **Entity** is used. I haven't tested it with mongo though.
|
||||
|
||||
**Note:** [extension repository](http://github.com/Atlantic18/DoctrineExtensions) contains all
|
||||
[documentation](http://github.com/Atlantic18/DoctrineExtensions/tree/master/doc) you may need
|
||||
to understand how you can use it in your projects.
|
||||
|
||||
<a name="alternative"></a>
|
||||
|
||||
## Alternative over configuration
|
||||
|
||||
You can use [StofDoctrineExtensionsBundle](http://github.com/stof/StofDoctrineExtensionsBundle) which is a wrapper of these extensions
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Make sure there are no *.orm.yml or *.orm.xml files for your Entities in your bundles Resources/config/doctrine directory. With those files in place the annotations won't be taken into account.
|
||||
674
vendor/gedmo/doctrine-extensions/doc/timestampable.md
vendored
Normal file
674
vendor/gedmo/doctrine-extensions/doc/timestampable.md
vendored
Normal file
|
|
@ -0,0 +1,674 @@
|
|||
# Timestampable behavior extension for Doctrine 2
|
||||
|
||||
**Timestampable** behavior will automate the update of date fields
|
||||
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.
|
||||
|
||||
Features:
|
||||
|
||||
- Automatic predefined date 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
|
||||
|
||||
Update **2012-06-26**
|
||||
|
||||
- Allow multiple values for on="change"
|
||||
|
||||
Update **2012-03-10**
|
||||
|
||||
- Add [Timestampable traits](#traits)
|
||||
|
||||
Update **2011-04-04**
|
||||
|
||||
- Made single listener, one instance can be used for any object manager
|
||||
and any number of them
|
||||
|
||||
**Note:**
|
||||
- Last update date: **2012-01-02**
|
||||
|
||||
**Portability:**
|
||||
|
||||
- **Timestampable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle)
|
||||
ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions
|
||||
|
||||
This article will cover the basic installation and functionality of **Timestampable** 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)
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
## Timestampable Entity example:
|
||||
|
||||
### Timestampable annotations:
|
||||
- **@Gedmo\Mapping\Annotation\Timestampable** this annotation tells that this column is timestampable
|
||||
by default it updates this column on update. If column is not date, datetime or time
|
||||
type 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**
|
||||
|
||||
**Note:** that Timestampable interface is not necessary, except in cases where
|
||||
you need to identify entity as being Timestampable. The metadata is loaded only once then
|
||||
cache is activated
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="body", type="string")
|
||||
*/
|
||||
private $body;
|
||||
|
||||
/**
|
||||
* @var \DateTime $created
|
||||
*
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/**
|
||||
* @var \DateTime $updated
|
||||
*
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private $updated;
|
||||
|
||||
/**
|
||||
* @var \DateTime $contentChanged
|
||||
*
|
||||
* @ORM\Column(name="content_changed", type="datetime", nullable=true)
|
||||
* @Gedmo\Timestampable(on="change", field={"title", "body"})
|
||||
*/
|
||||
private $contentChanged;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->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 getCreated()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function getUpdated()
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
public function getContentChanged()
|
||||
{
|
||||
return $this->contentChanged;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="document-mapping"></a>
|
||||
|
||||
## Timestampable Document example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Document;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
|
||||
/**
|
||||
* @ODM\Document(collection="articles")
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ODM\Id */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $body;
|
||||
|
||||
/**
|
||||
* @var date $created
|
||||
*
|
||||
* @ODM\Date
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/**
|
||||
* @var date $updated
|
||||
*
|
||||
* @ODM\Date
|
||||
* @Gedmo\Timestampable
|
||||
*/
|
||||
private $updated;
|
||||
|
||||
/**
|
||||
* @var \DateTime $contentChanged
|
||||
*
|
||||
* @ODM\Date
|
||||
* @Gedmo\Timestampable(on="change", field={"title", "body"})
|
||||
*/
|
||||
private $contentChanged;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->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 getCreated()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function getUpdated()
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
public function getContentChanged()
|
||||
{
|
||||
return $this->contentChanged;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now on update and creation these annotated fields will be automatically updated
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## Yaml mapping example:
|
||||
|
||||
Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
|
||||
|
||||
```yaml
|
||||
---
|
||||
Entity\Article:
|
||||
type: entity
|
||||
table: articles
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
title:
|
||||
type: string
|
||||
length: 64
|
||||
created:
|
||||
type: date
|
||||
gedmo:
|
||||
timestampable:
|
||||
on: create
|
||||
updated:
|
||||
type: datetime
|
||||
gedmo:
|
||||
timestampable:
|
||||
on: update
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
|
||||
<entity name="Mapping\Fixture\Xml\Timestampable" table="timestampables">
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="created" type="datetime">
|
||||
<gedmo:timestampable on="create"/>
|
||||
</field>
|
||||
<field name="updated" type="datetime">
|
||||
<gedmo:timestampable on="update"/>
|
||||
</field>
|
||||
<field name="published" type="datetime" nullable="true">
|
||||
<gedmo:timestampable on="change" field="status.title" value="Published"/>
|
||||
</field>
|
||||
|
||||
<many-to-one field="status" target-entity="Status">
|
||||
<join-column name="status_id" referenced-column-name="id"/>
|
||||
</many-to-one>
|
||||
</entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="advanced-examples"></a>
|
||||
|
||||
## Advanced examples:
|
||||
|
||||
### Using dependency of property changes
|
||||
|
||||
Add another entity which would represent Article Type:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Type
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Article", mappedBy="type")
|
||||
*/
|
||||
private $articles;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now update the Article Entity to reflect published date on Type change:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @var \DateTime $created
|
||||
*
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/**
|
||||
* @var \DateTime $updated
|
||||
*
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private $updated;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Type", inversedBy="articles")
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var \DateTime $published
|
||||
*
|
||||
* @ORM\Column(type="datetime", nullable=true)
|
||||
* @Gedmo\Timestampable(on="change", field="type.title", value="Published")
|
||||
*
|
||||
* or for example
|
||||
* @Gedmo\Timestampable(on="change", field="type.title", value={"Published", "Closed"})
|
||||
*/
|
||||
private $published;
|
||||
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getCreated()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function getUpdated()
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
public function getPublished()
|
||||
{
|
||||
return $this->published;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
|
||||
|
||||
``` yaml
|
||||
---
|
||||
Entity\Article:
|
||||
type: entity
|
||||
table: articles
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
title:
|
||||
type: string
|
||||
length: 64
|
||||
created:
|
||||
type: date
|
||||
gedmo:
|
||||
timestampable:
|
||||
on: create
|
||||
updated:
|
||||
type: datetime
|
||||
gedmo:
|
||||
timestampable:
|
||||
on: update
|
||||
published:
|
||||
type: datetime
|
||||
gedmo:
|
||||
timestampable:
|
||||
on: change
|
||||
field: type.title
|
||||
value: Published
|
||||
manyToOne:
|
||||
type:
|
||||
targetEntity: Entity\Type
|
||||
inversedBy: articles
|
||||
```
|
||||
|
||||
Now few operations to get it all done:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Article;
|
||||
$article->setTitle('My Article');
|
||||
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
// article: $created, $updated 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: $published, $updated were set
|
||||
|
||||
$article->getPublished()->format('Y-m-d'); // the date article type changed to published
|
||||
```
|
||||
|
||||
Easy like that, any suggestions on improvements are very welcome
|
||||
|
||||
### Creating a UTC DateTime type that stores your datetimes in UTC
|
||||
|
||||
First, we define our custom data type (note the type name is datetime and the type extends DateTimeType which simply overrides the default Doctrine type):
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
namespace Acme\DoctrineExtensions\DBAL\Types;
|
||||
|
||||
use Doctrine\DBAL\Types\DateTimeType;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\ConversionException;
|
||||
|
||||
class UTCDateTimeType extends DateTimeType
|
||||
{
|
||||
static private $utc = null;
|
||||
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_null(self::$utc)) {
|
||||
self::$utc = new \DateTimeZone('UTC');
|
||||
}
|
||||
|
||||
$value->setTimeZone(self::$utc);
|
||||
|
||||
return $value->format($platform->getDateTimeFormatString());
|
||||
}
|
||||
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
if ($value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (is_null(self::$utc)) {
|
||||
self::$utc = new \DateTimeZone('UTC');
|
||||
}
|
||||
|
||||
$val = \DateTime::createFromFormat($platform->getDateTimeFormatString(), $value, self::$utc);
|
||||
|
||||
if (!$val) {
|
||||
throw ConversionException::conversionFailed($value, $this->getName());
|
||||
}
|
||||
|
||||
return $val;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now in Symfony2, we register and override the **datetime** type. **WARNING:** this will override the **datetime** type for all your entities and for all entities in external bundles or extensions, so if you have some entities that require the standard **datetime** type from Doctrine, you must modify the above type and use a different name (such as **utcdatetime**). Additionally, you'll need to modify **Timestampable** so that it includes **utcdatetime** as a valid type.
|
||||
|
||||
``` yaml
|
||||
doctrine:
|
||||
dbal:
|
||||
types:
|
||||
datetime: Acme\DoctrineExtensions\DBAL\Types\UTCDateTimeType
|
||||
```
|
||||
|
||||
And our Entity properties look as expected:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
/**
|
||||
* @var \DateTime $dateCreated
|
||||
*
|
||||
* @ORM\Column(name="date_created", type="datetime")
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
*/
|
||||
private $dateCreated;
|
||||
|
||||
/**
|
||||
* @var \DateTime $dateLastModified
|
||||
*
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
* @ORM\Column(name="date_last_modified", type="datetime")
|
||||
*/
|
||||
private $dateLastModified;
|
||||
```
|
||||
|
||||
Now, in our view (suppose we are using Symfony2 and Twig), we can display the datetime (which is persisted in UTC format) in our user's time zone:
|
||||
|
||||
``` twig
|
||||
{{ myEntity.dateCreated | date("d/m/Y g:i a", app.user.timezone) }}
|
||||
```
|
||||
|
||||
Or if the user does not have a timezone, we could expand that to use a system/app/PHP default timezone.
|
||||
|
||||
[doctrine_custom_datetime_type]: http://www.doctrine-project.org/docs/orm/2.0/en/cookbook/working-with-datetime.html#handling-different-timezones-with-the-datetime-type "Handling different Timezones with the DateTime Type"
|
||||
|
||||
This example is based off [Handling different Timezones with the DateTime Type][doctrine_custom_datetime_type] - however that example may be outdated because it contains some obviously invalid PHP from the TimeZone class.
|
||||
|
||||
<a name="traits"></a>
|
||||
|
||||
## Traits
|
||||
|
||||
You can use timestampable traits for quick **createdAt** **updatedAt** timestamp 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
|
||||
<?php
|
||||
namespace Timestampable\Fixture;
|
||||
|
||||
use Gedmo\Timestampable\Traits\TimestampableEntity;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class UsingTrait
|
||||
{
|
||||
/**
|
||||
* Hook timestampable behavior
|
||||
* updates createdAt, updatedAt fields
|
||||
*/
|
||||
use TimestampableEntity;
|
||||
|
||||
/**
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=128)
|
||||
*/
|
||||
private $title;
|
||||
}
|
||||
```
|
||||
|
||||
Traits are very simple and if you use different field names I recommend to simply create your
|
||||
own ones based per project. These ones are standing as an example.
|
||||
234
vendor/gedmo/doctrine-extensions/doc/transaction-safety.md
vendored
Normal file
234
vendor/gedmo/doctrine-extensions/doc/transaction-safety.md
vendored
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
### Making safe transactions in concurrent environment
|
||||
|
||||
Some extensions are using atomic updates, which need some extra attention
|
||||
in order to maintain data integrity. If you are using one of these extensions
|
||||
listed below, you should read further and take the appropriate actions:
|
||||
|
||||
- Sortable
|
||||
- Tree - NestedSet strategy
|
||||
- Tree - MaterializedPath strategy
|
||||
|
||||
So let me explain first, why and what actions are needed to be applied to
|
||||
maintain your data integrity.
|
||||
|
||||
Imagine two concurrent requests are being issued with some entity updates which does
|
||||
some actions for one or more of these extensions listed. One request starts a transaction
|
||||
to do atomic updates and another at the same time, while the first transaction executes
|
||||
starts the second transaction. The second transaction might be performing updates based
|
||||
on data which is outdated or even still running in the first transaction. The possibility
|
||||
to have broken data increases with concurrency.
|
||||
|
||||
We need to lock one transaction so the other would wait until the first finishes and then we can
|
||||
begin the second one which in turn would lock the third one if there would be any.
|
||||
|
||||
**NOTE:** it is not enough to simply have a transaction.
|
||||
|
||||
So how we can achieve this? The simplest solution is [pessimistic locking](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/transactions-and-concurrency.html#pessimistic-locking) which is supported by ORM.
|
||||
|
||||
So how we can use it correctly to maintain our transactions safe from one another. Lets say we have two entity types in
|
||||
our application:
|
||||
|
||||
- **Shop** - lets say our ecommerce platform we are creating, supports multiple shops.
|
||||
- **Category** - every shop might have a different category set for products and other features.
|
||||
|
||||
So the **Category** should be a **nested set tree** strategy based, where atomic updates might be executed
|
||||
if a category is being moved, inserted or removed.
|
||||
|
||||
To start with, I'll make the simple definitions of these two entities:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Shop
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $name;
|
||||
|
||||
public function setName($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It should have owner and so many more attributes, but lets keep it simple. Here follows Category:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @Gedmo\Tree(type="nested")
|
||||
* @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
|
||||
*/
|
||||
class Category
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeLeft
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $lft;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeRight
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $rgt;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeParent
|
||||
* @ORM\ManyToOne(targetEntity="Category")
|
||||
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Shop")
|
||||
*/
|
||||
private $shop;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeRoot
|
||||
* @ORM\Column(type="integer", nullable=true)
|
||||
*/
|
||||
private $root;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeLevel
|
||||
* @ORM\Column(name="lvl", type="integer")
|
||||
*/
|
||||
private $level;
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setParent(Category $parent = null)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function getLevel()
|
||||
{
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
public function setShop(Shop $shop)
|
||||
{
|
||||
$this->shop = $shop;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getShop()
|
||||
{
|
||||
return $this->shop;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**NOTE:** it would be perfect if we could use tree root as a shop relation. But it is not currently supported and
|
||||
might be available only in next versions.
|
||||
|
||||
Now everytime we do **insert**, **move** or **remove** actions for Category:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
|
||||
use Doctrine\DBAL\LockMode;
|
||||
|
||||
class CategoryController extends Controller
|
||||
{
|
||||
function postCategoryAction($currentShopId)
|
||||
{
|
||||
$em = $this->getEntityManager();
|
||||
$conn = $em->getConnection();
|
||||
$categoryRepository = $em->getRepository("App\Entity\Category");
|
||||
// start transaction
|
||||
$conn->beginTransaction();
|
||||
try {
|
||||
// select shop for update - locks it for any read attempts until this transaction ends
|
||||
$shop = $em->find("App\Entity\Shop", $currentShopId, LockMode::PESSIMISTIC_WRITE);
|
||||
|
||||
// create a new category
|
||||
$category = new Category;
|
||||
$category->setTitle($_POST["title"]);
|
||||
$category->setShop($shop);
|
||||
$parent = $categoryRepository->findOneById($_POST["parent_id"]);
|
||||
|
||||
// persist and flush
|
||||
$categoryRepository->persistAsFirstChildOf($category, $parent);
|
||||
$em->flush();
|
||||
|
||||
$conn->commit();
|
||||
} catch (Exception $e) {
|
||||
$conn->rollback();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// if all went well, we can set flash message or whatever
|
||||
// other operations which attempts to select in lock mode, will wait till this transaction ends.
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You may separate locking transaction to run callback function and make it as a service to abstract and prevent
|
||||
code duplication. Anyway, my advice would be to use only one transaction per request and best inside controller
|
||||
directly, where you would ensure that all operations performed during the action can be safely rolled back.
|
||||
|
||||
Also to use this kind of locking, you need an entity which is necessary to read on concurrent request which attempts
|
||||
to update the same tree. In this example, **Shop** entity fits the bill perfectly. Otherwise you need to find a way to
|
||||
safely lock the tree table.
|
||||
|
||||
The point of this example is: that concurrently atomic updates, might cause other parallel actions to use outdated
|
||||
information, based on which it may perform falsely calculated consequent updates. And you need to prevent this from
|
||||
happening in order to maintain your data. Extensions and ORM cannot perform such actions automatically.
|
||||
|
||||
894
vendor/gedmo/doctrine-extensions/doc/translatable.md
vendored
Normal file
894
vendor/gedmo/doctrine-extensions/doc/translatable.md
vendored
Normal file
|
|
@ -0,0 +1,894 @@
|
|||
# Translatable behavior extension for Doctrine 2
|
||||
|
||||
**Translatable** behavior offers a very handy solution for translating specific record fields
|
||||
in different languages. Further more, it loads the translations automatically for a locale
|
||||
currently used, which can be set to **Translatable Listener** on it`s initialization or later
|
||||
for other cases through the **Entity** itself
|
||||
|
||||
Features:
|
||||
|
||||
- Automatic storage of translations in database
|
||||
- ORM and ODM support using same listener
|
||||
- Automatic translation of Entity or Document fields when loaded
|
||||
- ORM query can use **hint** to translate all records without issuing additional queries
|
||||
- Can be nested with other behaviors
|
||||
- Annotation, Yaml and Xml mapping support for extensions
|
||||
|
||||
**2012-01-28**
|
||||
|
||||
- Created personal translation which maps through real foreign key
|
||||
constraint. This dramatically improves the management of translations
|
||||
|
||||
**2012-01-04**
|
||||
|
||||
- Refactored translatable to be able to persist, update many translations
|
||||
using repository, [issue #224](https://github.com/Atlantic18/DoctrineExtensions/issues/224)
|
||||
|
||||
**2011-12-11**
|
||||
|
||||
- Added more useful translation query hints: Override translatable locale, inner join
|
||||
translations instead left join, override translation fallback
|
||||
|
||||
**2011-11-08**
|
||||
|
||||
- Thanks to [@acasademont](https://github.com/acasademont) Translatable now does not store translations for default locale. It is always left as original record value.
|
||||
So be sure you do not change your default locale per project or per data migration. This way
|
||||
it is more rational and unnecessary to store it additionally in translation table.
|
||||
|
||||
Update **2011-04-21**
|
||||
|
||||
- Implemented multiple translation persistence through repository
|
||||
|
||||
Update **2011-04-16**
|
||||
|
||||
- Made an ORM query **hint** to hook into any select type query, which will join the translations
|
||||
and let you **filter, order or search** by translated fields directly. It also will translate
|
||||
all selected **collections or simple components** without issuing additional queries. It also
|
||||
supports translation fallbacks
|
||||
- For performance reasons, translation fallbacks are disabled by default
|
||||
|
||||
Update **2011-04-04**
|
||||
|
||||
- Made single listener, one instance can be used for any object manager
|
||||
and any number of them
|
||||
|
||||
**Note list:**
|
||||
|
||||
- Public [Translatable repository](http://github.com/Atlantic18/DoctrineExtensions "Translatable extension on Github") is available on github
|
||||
- Using other extensions on the same Entity fields may result in unexpected way
|
||||
- May impact your application performance since it does an additional query for translation if loaded without query hint
|
||||
- Last update date: **2012-02-15**
|
||||
|
||||
**Portability:**
|
||||
|
||||
- **Translatable** is now available as [Bundle](http://github.com/stof/StofDoctrineExtensionsBundle)
|
||||
ported to **Symfony2** by **Christophe Coevoet**, together with all other extensions
|
||||
|
||||
This article will cover the basic installation and functionality of **Translatable** behavior
|
||||
|
||||
Content:
|
||||
|
||||
- [Including](#including-extension) the extension
|
||||
- Entity [example](#entity-domain-object)
|
||||
- Document [example](#document-domain-object)
|
||||
- [Yaml](#yaml-mapping) mapping example
|
||||
- [Xml](#xml-mapping) mapping example
|
||||
- Basic usage [examples](#basic-examples)
|
||||
- [Persisting](#multi-translations) multiple translations
|
||||
- Using ORM query [hint](#orm-query-hint)
|
||||
- Advanced usage [examples](#advanced-examples)
|
||||
- Personal [translations](#personal-translations)
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## 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.
|
||||
|
||||
### Translatable annotations:
|
||||
- **@Gedmo\Mapping\Annotation\Translatable** it will **translate** this field
|
||||
- **@Gedmo\Mapping\Annotation\TranslationEntity(class="my\class")** it will use this class to store **translations** generated
|
||||
- **@Gedmo\Mapping\Annotation\Locale or @Gedmo\Mapping\Annotation\Language** this will identify this column as **locale** or **language**
|
||||
used to override the global locale
|
||||
|
||||
<a name="entity-domain-object"></a>
|
||||
|
||||
## Translatable Entity example:
|
||||
|
||||
**Note:** that Translatable interface is not necessary, except in cases where
|
||||
you need to identify an entity as being Translatable. The metadata is loaded only once when
|
||||
cache is activated
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Translatable\Translatable;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="articles")
|
||||
* @ORM\Entity
|
||||
*/
|
||||
class Article implements Translatable
|
||||
{
|
||||
/** @ORM\Id @ORM\GeneratedValue @ORM\Column(type="integer") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(name="title", type="string", length=128)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(name="content", type="text")
|
||||
*/
|
||||
private $content;
|
||||
|
||||
/**
|
||||
* @Gedmo\Locale
|
||||
* Used locale to override Translation listener`s locale
|
||||
* this is not a mapped field of entity metadata, just a simple property
|
||||
*/
|
||||
private $locale;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setTranslatableLocale($locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="document-domain-object"></a>
|
||||
|
||||
## Translatable Document example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Document;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
use Gedmo\Translatable\Translatable;
|
||||
|
||||
/**
|
||||
* @ODM\Document(collection="articles")
|
||||
*/
|
||||
class Article implements Translatable
|
||||
{
|
||||
/** @ODM\Id */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
private $content;
|
||||
|
||||
/**
|
||||
* @Gedmo\Locale
|
||||
* Used locale to override Translation listener`s locale
|
||||
* this is not a mapped field of entity metadata, just a simple property
|
||||
*/
|
||||
private $locale;
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setContent($content)
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
public function getContent()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setTranslatableLocale($locale)
|
||||
{
|
||||
$this->locale = $locale;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## Yaml mapping example
|
||||
|
||||
Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
|
||||
|
||||
```
|
||||
---
|
||||
Entity\Article:
|
||||
type: entity
|
||||
table: articles
|
||||
gedmo:
|
||||
translation:
|
||||
locale: localeField
|
||||
# using specific personal translation class:
|
||||
# entity: Translatable\Fixture\CategoryTranslation
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
title:
|
||||
type: string
|
||||
length: 64
|
||||
gedmo:
|
||||
- translatable
|
||||
content:
|
||||
type: text
|
||||
gedmo:
|
||||
- translatable
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
|
||||
<entity name="Mapping\Fixture\Xml\Translatable" table="translatables">
|
||||
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="title" type="string" length="128">
|
||||
<gedmo:translatable/>
|
||||
</field>
|
||||
<field name="content" type="text">
|
||||
<gedmo:translatable/>
|
||||
</field>
|
||||
|
||||
<gedmo:translation entity="Gedmo\Translatable\Entity\Translation" locale="locale"/>
|
||||
|
||||
</entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="basic-examples"></a>
|
||||
|
||||
## Basic usage examples:
|
||||
|
||||
Currently a global locale used for translations is "en_us" which was
|
||||
set in **TranslationListener** globally. To save article with its translations:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = new Entity\Article;
|
||||
$article->setTitle('my title in en');
|
||||
$article->setContent('my content in en');
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
This inserted an article and inserted the translations for it in "en_us" locale
|
||||
only if **en_us** is not the [default locale](#advanced-examples) in case if default locale
|
||||
matches current locale - it uses original record value as translation
|
||||
|
||||
Now lets update our article in different locale:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// first load the article
|
||||
$article = $em->find('Entity\Article', 1 /*article id*/);
|
||||
$article->setTitle('my title in de');
|
||||
$article->setContent('my content in de');
|
||||
$article->setTranslatableLocale('de_de'); // change locale
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
This updated an article and inserted the translations for it in "de_de" locale
|
||||
To see and load all translations of **Translatable** Entity:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// reload in different language
|
||||
$article = $em->find('Entity\Article', 1 /*article id*/);
|
||||
$article->setLocale('ru_ru');
|
||||
$em->refresh($article);
|
||||
|
||||
$article = $em->find('Entity\Article', 1 /*article id*/);
|
||||
$repository = $em->getRepository('Gedmo\Translatable\Entity\Translation');
|
||||
$translations = $repository->findTranslations($article);
|
||||
/* $translations contains:
|
||||
Array (
|
||||
[de_de] => Array
|
||||
(
|
||||
[title] => my title in de
|
||||
[content] => my content in de
|
||||
)
|
||||
|
||||
[en_us] => Array
|
||||
(
|
||||
[title] => my title in en
|
||||
[content] => my content in en
|
||||
)
|
||||
)*/
|
||||
```
|
||||
|
||||
As far as our global locale is now "en_us" and updated article has "de_de" values.
|
||||
Lets try to load it and it should be translated in English
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$article = $em->getRepository('Entity\Article')->find(1/* id of article */);
|
||||
echo $article->getTitle();
|
||||
// prints: "my title in en"
|
||||
echo $article->getContent();
|
||||
// prints: "my content in en"
|
||||
```
|
||||
|
||||
<a name="multi-translations"></a>
|
||||
|
||||
## Persisting multiple translations
|
||||
|
||||
Usually it is more convenient to persist more translations when creating
|
||||
or updating a record. **Translatable** allows to do that through translation repository.
|
||||
All additional translations will be tracked by listener and when the flush will be executed,
|
||||
it will update or persist all additional translations.
|
||||
|
||||
**Note:** these translations will not be processed as ordinary fields of your object,
|
||||
in case if you translate a **slug** additional translation will not know how to generate
|
||||
the slug, so the value as an additional translation should be processed when creating it.
|
||||
|
||||
### Example of multiple translations:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// persisting multiple translations, assume default locale is EN
|
||||
$repository = $em->getRepository('Gedmo\\Translatable\\Entity\\Translation');
|
||||
// it works for ODM also
|
||||
$article = new Article;
|
||||
$article->setTitle('My article en');
|
||||
$article->setContent('content en');
|
||||
|
||||
$repository->translate($article, 'title', 'de', 'my article de')
|
||||
->translate($article, 'content', 'de', 'content de')
|
||||
->translate($article, 'title', 'ru', 'my article ru')
|
||||
->translate($article, 'content', 'ru', 'content ru')
|
||||
;
|
||||
|
||||
$em->persist($article);
|
||||
$em->flush();
|
||||
|
||||
// updating same article also having one new translation
|
||||
|
||||
$repo
|
||||
->translate($article, 'title', 'lt', 'title lt')
|
||||
->translate($article, 'content', 'lt', 'content lt')
|
||||
->translate($article, 'title', 'ru', 'title ru change')
|
||||
->translate($article, 'content', 'ru', 'content ru change')
|
||||
->translate($article, 'title', 'en', 'title en (default locale) update')
|
||||
->translate($article, 'content', 'en', 'content en (default locale) update')
|
||||
;
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
<a name="orm-query-hint"></a>
|
||||
|
||||
## Using ORM query hint
|
||||
|
||||
By default, behind the scenes, when you load a record - translatable hooks into **postLoad**
|
||||
event and issues additional query to translate all fields. Imagine that, when you load a collection,
|
||||
it may issue a lot of queries just to translate those fields. Including array hydration,
|
||||
it is not possible to hook any **postLoad** event since it is not an
|
||||
entity being hydrated. These are the main reasons why **TranslationWalker** was created.
|
||||
|
||||
**TranslationWalker** uses a query **hint** to hook into any **select type query**,
|
||||
and when you execute the query, no matter which hydration method you use, it automatically
|
||||
joins the translations for all fields, so you could use ordering filtering or whatever you
|
||||
want on **translated fields** instead of original record fields.
|
||||
|
||||
And in result there is only one query for all this happiness.
|
||||
|
||||
If you use translation [fallbacks](#advanced-examples) it will be also in the same single
|
||||
query and during the hydration process it will replace the empty fields in case if they
|
||||
do not have a translation in currently used locale.
|
||||
|
||||
Now enough talking, here is an example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$dql = <<<___SQL
|
||||
SELECT a, c, u
|
||||
FROM Article a
|
||||
LEFT JOIN a.comments c
|
||||
JOIN c.author u
|
||||
WHERE a.title LIKE '%translated_title%'
|
||||
ORDER BY a.title
|
||||
___SQL;
|
||||
|
||||
$query = $em->createQuery($dql);
|
||||
// set the translation query hint
|
||||
$query->setHint(
|
||||
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
|
||||
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
|
||||
);
|
||||
|
||||
$articles = $query->getResult(); // object hydration
|
||||
$articles = $query->getArrayResult(); // array hydration
|
||||
```
|
||||
|
||||
And even a subselect:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$dql = <<<___SQL
|
||||
SELECT a, c, u
|
||||
FROM Article a
|
||||
LEFT JOIN a.comments c
|
||||
JOIN c.author u
|
||||
WHERE a.id IN (
|
||||
SELECT a2.id
|
||||
FROM Article a2
|
||||
WHERE a2.title LIKE '%something_translated%'
|
||||
AND a2.status = 1
|
||||
)
|
||||
ORDER BY a.title
|
||||
___SQL;
|
||||
|
||||
$query = $em->createQuery($dql);
|
||||
$query->setHint(
|
||||
\Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
|
||||
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
|
||||
);
|
||||
```
|
||||
|
||||
**NOTE:** if you use memcache or apc. You should set locale and other options like fallbacks
|
||||
to query through hints. Otherwise the query will be cached with a first used locale
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// locale
|
||||
$query->setHint(
|
||||
\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE,
|
||||
'en' // take locale from session or request etc.
|
||||
);
|
||||
// fallback
|
||||
$query->setHint(
|
||||
\Gedmo\Translatable\TranslatableListener::HINT_FALLBACK,
|
||||
1 // fallback to default values in case if record is not translated
|
||||
);
|
||||
|
||||
$articles = $query->getResult(); // object hydration
|
||||
```
|
||||
|
||||
There's no need for any words anymore.. right?
|
||||
I recommend you to use it extensively since it is a way better performance, even in
|
||||
cases where you need to load single translated entity.
|
||||
|
||||
**Note**: Even in **COUNT** select statements translations are joined to leave a
|
||||
possibility to filter by translated field, if you do not need it, just do not set
|
||||
the **hint**. Also take into account that it is not possible to translate components
|
||||
in **JOIN WITH** statement, example
|
||||
|
||||
```
|
||||
JOIN a.comments c WITH c.message LIKE '%will_not_be_translated%'`
|
||||
```
|
||||
|
||||
**Note**: any **find** related method calls cannot hook this hint automagically, we
|
||||
will use a different approach when **persister overriding feature** will be
|
||||
available in **Doctrine**
|
||||
|
||||
In case if **translation query walker** is used, you can additionally override:
|
||||
|
||||
### Overriding translation fallback
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$query->setHint(\Gedmo\Translatable\TranslatableListener::HINT_FALLBACK, 1);
|
||||
```
|
||||
|
||||
will fallback to default locale translations instead of empty values if used.
|
||||
And will override the translation listener setting for fallback.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$query->setHint(\Gedmo\Translatable\TranslatableListener::HINT_FALLBACK, 0);
|
||||
```
|
||||
|
||||
will do the opposite.
|
||||
|
||||
### Using inner join strategy
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$query->setHint(\Gedmo\Translatable\TranslatableListener::HINT_INNER_JOIN, true);
|
||||
```
|
||||
|
||||
will use **INNER** joins
|
||||
for translations instead of **LEFT** joins, so that in case if you do not want untranslated
|
||||
records in your result set for instance.
|
||||
|
||||
### Overriding translatable locale
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$query->setHint(\Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'en');
|
||||
```
|
||||
|
||||
would override the translation locale used to translate the resultset.
|
||||
|
||||
**Note:** all these query hints lasts only for the specific query.
|
||||
|
||||
<a name="advanced-examples"></a>
|
||||
|
||||
## Advanced examples:
|
||||
|
||||
### Default locale
|
||||
|
||||
In some cases we need a default translation as a fallback if record does not have
|
||||
a translation on globally used locale. In that case Translation Listener takes the
|
||||
current value of Entity. So if **default locale** is specified and it matches the
|
||||
locale in which record is being translated - it will not create extra translation
|
||||
but use original values instead. If translation fallback is set to **false** it
|
||||
will fill untranslated values as blanks
|
||||
|
||||
To set the default locale:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$translatableListener->setDefaultLocale('en_us');
|
||||
```
|
||||
|
||||
To set translation fallback:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$translatableListener->setTranslationFallback(true); // default is false
|
||||
```
|
||||
|
||||
**Note**: Default locale should be set on the **TranslatableListener** initialization
|
||||
once, since it can impact your current records if it will be changed. As it
|
||||
will not store extra record in translation table by default.
|
||||
|
||||
If you need to store translation in default locale, set:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
$translatableListener->setPersistDefaultLocaleTranslation(true); // default is false
|
||||
```
|
||||
|
||||
This would always store translations in all locales, also keeping original record
|
||||
translated field values in default locale set.
|
||||
|
||||
### Translation Entity
|
||||
|
||||
In some cases if there are thousands of records or even more.. we would like to
|
||||
have a single table for translations of this Entity in order to increase the performance
|
||||
on translation loading speed. This example will show how to specify a different Entity for
|
||||
your translations by extending the mapped superclass.
|
||||
|
||||
ArticleTranslation Entity:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity\Translation;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Translatable\Entity\MappedSuperclass\AbstractTranslation;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="article_translations", indexes={
|
||||
* @ORM\Index(name="article_translation_idx", columns={"locale", "object_class", "field", "foreign_key"})
|
||||
* })
|
||||
* @ORM\Entity(repositoryClass="Gedmo\Translatable\Entity\Repository\TranslationRepository")
|
||||
*/
|
||||
class ArticleTranslation extends AbstractTranslation
|
||||
{
|
||||
/**
|
||||
* All required columns are mapped through inherited superclass
|
||||
*/
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** We specified the repository class to be used from extension.
|
||||
It is handy for specific methods common to the Translation Entity
|
||||
|
||||
**Note:** This Entity will be used instead of default Translation Entity
|
||||
only if we specify a class annotation @Gedmo\TranslationEntity(class="my\translation\entity"):
|
||||
|
||||
``` php
|
||||
<?php
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Table(name="articles")
|
||||
* @ORM\Entity
|
||||
* @Gedmo\TranslationEntity(class="Entity\Translation\ArticleTranslation")
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Now all translations of Article will be stored and queried from specific table
|
||||
|
||||
<a name="personal-translations"></a>
|
||||
|
||||
## Personal translations
|
||||
|
||||
Translatable has **AbstractPersonalTranslation** mapped superclass, which must
|
||||
be extended and mapped based on your **entity** which you want to translate.
|
||||
Note: translations are not automapped because of user preference based on cascades
|
||||
or other possible choices, which user can make.
|
||||
Personal translations uses foreign key constraint which is fully managed by ORM and
|
||||
allows to have a collection of related translations. User can use it anyway he likes, etc.:
|
||||
implementing array access on entity, using left join to fill collection and so on.
|
||||
|
||||
Note: that [query hint](#orm-query-hint) will work on personal translations the same way.
|
||||
You can always use a left join like for standard doctrine collections.
|
||||
|
||||
Usage example:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\TranslationEntity(class="Entity\CategoryTranslation")
|
||||
*/
|
||||
class Category
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity="CategoryTranslation",
|
||||
* mappedBy="object",
|
||||
* cascade={"persist", "remove"}
|
||||
* )
|
||||
*/
|
||||
private $translations;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->translations = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getTranslations()
|
||||
{
|
||||
return $this->translations;
|
||||
}
|
||||
|
||||
public function addTranslation(CategoryTranslation $t)
|
||||
{
|
||||
if (!$this->translations->contains($t)) {
|
||||
$this->translations[] = $t;
|
||||
$t->setObject($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getTitle();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now the translation entity for the Category:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="category_translations",
|
||||
* uniqueConstraints={@ORM\UniqueConstraint(name="lookup_unique_idx", columns={
|
||||
* "locale", "object_id", "field"
|
||||
* })}
|
||||
* )
|
||||
*/
|
||||
class CategoryTranslation extends AbstractPersonalTranslation
|
||||
{
|
||||
/**
|
||||
* Convenient constructor
|
||||
*
|
||||
* @param string $locale
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*/
|
||||
public function __construct($locale, $field, $value)
|
||||
{
|
||||
$this->setLocale($locale);
|
||||
$this->setField($field);
|
||||
$this->setContent($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Category", inversedBy="translations")
|
||||
* @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
protected $object;
|
||||
}
|
||||
```
|
||||
|
||||
Some example code to persist with translations:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// assumes default locale is "en"
|
||||
$food = new Entity\Category;
|
||||
$food->setTitle('Food');
|
||||
$food->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Maistas'));
|
||||
|
||||
$fruits = new Entity\Category;
|
||||
$fruits->setParent($food);
|
||||
$fruits->setTitle('Fruits');
|
||||
$fruits->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Vaisiai'));
|
||||
$fruits->addTranslation(new Entity\CategoryTranslation('ru', 'title', 'rus trans'));
|
||||
|
||||
$em->persist($food);
|
||||
$em->persist($fruits);
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
This would create translations for english and lithuanian, and for fruits, **ru** additionally.
|
||||
|
||||
Easy like that, any suggestions on improvements are very welcome
|
||||
|
||||
|
||||
### Example code to use Personal Translations with (Symfony2 Sonata) i18n Forms:
|
||||
|
||||
Suppose you have a Sonata Backend with a simple form like:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
protected function configureFormFields(FormMapper $formMapper) {
|
||||
$formMapper
|
||||
->with('General')
|
||||
->add('title', 'text')
|
||||
->end()
|
||||
;
|
||||
}
|
||||
```
|
||||
|
||||
Then you can turn it into an i18n Form by providing the following changes.
|
||||
|
||||
``` php
|
||||
<?php
|
||||
protected function configureFormFields(FormMapper $formMapper)
|
||||
{
|
||||
$formMapper
|
||||
->with('General')
|
||||
->add('title', 'translatable_field', array(
|
||||
'field' => 'title',
|
||||
'personal_translation' => 'ExampleBundle\Entity\Translation\ProductTranslation',
|
||||
'property_path' => 'translations',
|
||||
))
|
||||
->end()
|
||||
;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
To accomplish this you can add the following code in your bundle:
|
||||
|
||||
https://gist.github.com/2437078
|
||||
|
||||
<Bundle>/Form/TranslatedFieldType.php
|
||||
<Bundle>/Form/EventListener/addTranslatedFieldSubscriber.php
|
||||
<Bundle>/Resources/services.yml
|
||||
|
||||
Then you can change to your needs:
|
||||
|
||||
``` php
|
||||
'field' => 'title', //you need to provide which field you wish to translate
|
||||
'personal_translation' => 'ExampleBundle\Entity\Translation\ProductTranslation', //the personal translation entity
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Translations field type using Personal Translations with Symfony2:
|
||||
|
||||
You can use [A2lixTranslationFormBundle](https://github.com/a2lix/TranslationFormBundle) to facilitate your translations.
|
||||
1339
vendor/gedmo/doctrine-extensions/doc/tree.md
vendored
Normal file
1339
vendor/gedmo/doctrine-extensions/doc/tree.md
vendored
Normal file
File diff suppressed because it is too large
Load diff
467
vendor/gedmo/doctrine-extensions/doc/uploadable.md
vendored
Normal file
467
vendor/gedmo/doctrine-extensions/doc/uploadable.md
vendored
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
# Uploadable behavior extension for Doctrine 2
|
||||
|
||||
**Uploadable** behavior provides the tools to manage the persistence of files with
|
||||
Doctrine 2, including automatic handling of moving, renaming and removal of files and other features.
|
||||
|
||||
Features:
|
||||
|
||||
- Extension moves, removes and renames files according to configuration automatically
|
||||
- Lots of options: Allow overwrite, append a number if file exists, filename generators, post-move callbacks, etc.
|
||||
- It can be extended to work not only with uploaded files, but with files coming from any source (an URL, another
|
||||
file in the same server, etc).
|
||||
- Validation of size and mime type
|
||||
|
||||
Content:
|
||||
|
||||
- [Including](#including-extension) the extension
|
||||
- Entity [example](#entity-mapping)
|
||||
- [Yaml](#yaml-mapping) mapping example
|
||||
- [Xml](#xml-mapping) mapping example
|
||||
- Usage [examples](#usage)
|
||||
- [Using](#additional-usages) the extension to handle not only uploaded files
|
||||
- [Custom](#custom-mime-type-guessers) mime type guessers
|
||||
|
||||
<a name="including-extension"></a>
|
||||
|
||||
## Setup and autoloading
|
||||
|
||||
Read the [documentation](http://github.com/l3pp4rd/DoctrineExtensions/blob/master/doc/annotations.md#em-setup)
|
||||
or check the [example code](http://github.com/l3pp4rd/DoctrineExtensions/tree/master/example)
|
||||
on how to setup and use the extensions in most optimized way.
|
||||
|
||||
|
||||
<a name="entity-mapping"></a>
|
||||
|
||||
## Uploadable Entity example:
|
||||
|
||||
### Uploadable annotations:
|
||||
1. **@Gedmo\Mapping\Annotation\Uploadable** this class annotation tells if a class is Uploadable. Available configuration options:
|
||||
* **allowOverwrite** - If this option is true, it will overwrite a file if it already exists. If you set "false", an
|
||||
exception will be thrown. Default: false
|
||||
* **appendNumber** - If this option is true and "allowOverwrite" is false, in the case that the file already exists,
|
||||
it will append a number to the filename. Example: if you're uploading a file named "test.txt", if the file already
|
||||
exists and this option is true, the extension will modify the name of the uploaded file to "test-1.txt", where "1"
|
||||
could be any number. The extension will check if the file exists until it finds a filename with a number as its postfix that is not used.
|
||||
If you use a filename generator and this option is true, it will append a number to the filename anyway if a file with
|
||||
the same name already exists.
|
||||
Default value: false
|
||||
* **path** - This option expects a string containing the path where the files represented by this entity will be moved.
|
||||
Default: "". Path can be set in other ways: From the listener or from a method. More details later.
|
||||
* **pathMethod** - Similar to option "path", but this time it represents the name of a method on the entity that
|
||||
will return the path to which the files represented by this entity will be moved. This is useful in several cases.
|
||||
For example, you can set specific paths for specific entities, or you can get the path from other sources (like a
|
||||
framework configuration) instead of hardcoding it in the entity. Default: "". As first argument this method takes
|
||||
default path, so you can return path relative to default.
|
||||
* **callback** - This option allows you to set a method name. If this option is set, the method will be called after
|
||||
the file is moved. Default value: "". As first argument, this method can receive an array with information about the uploaded file, which
|
||||
includes the following keys:
|
||||
1. **fileName**: The filename.
|
||||
2. **fileExtension**: The extension of the file (including the dot). Example: .jpg
|
||||
3. **fileWithoutExt**: The filename without the extension.
|
||||
4. **filePath**: The file path. Example: /my/path/filename.jpg
|
||||
5. **fileMimeType**: The mime-type of the file. Example: text/plain.
|
||||
6. **fileSize**: Size of the file in bytes. Example: 140000.
|
||||
* **filenameGenerator**: This option allows you to set a filename generator for the file. There are two already included
|
||||
by the extension: **SHA1**, which generates a sha1 filename for the file, and **ALPHANUMERIC**, which "normalizes"
|
||||
the filename, leaving only alphanumeric characters in the filename, and replacing anything else with a "-". You can
|
||||
even create your own FilenameGenerator class (implementing the Gedmo\Uploadable\FilenameGenerator\FilenameGeneratorInterface) and set this option with the
|
||||
fully qualified class name. The other option available is "NONE" which, as you may guess, means no generation for the
|
||||
filename will occur. Default: "NONE".
|
||||
* **maxSize**: This option allows you to set a maximum size for the file in bytes. If file size exceeds the value
|
||||
set in this configuration, an exception of type "UploadableMaxSizeException" will be thrown. By default, its value is set to 0, meaning
|
||||
that no size validation will occur.
|
||||
* **allowedTypes**: With this option you can set a comma-separated list of allowed mime types for the file. The extension
|
||||
will use a simple mime type guesser to guess the file type, and then it will compare it to the list of allowed types.
|
||||
If the mime type is not valid, then an exception of type "UploadableInvalidMimeTypeException" will be thrown. If you
|
||||
set this option, you can't set the **disallowedTypes** option described next. By default, no validation of mime type
|
||||
occurs. If you want to use a custom mime type guesser, see [this](#custom-mime-type-guessers).
|
||||
* **disallowedTypes**: Similar to the option **allowedTypes**, but with this one you configure a "black list" of
|
||||
mime types. If the mime type of the file is on this list, n exception of type "UploadableInvalidMimeTypeException" will be thrown. If you
|
||||
set this option, you can't set the **allowedTypes** option described above. By default, no validation of mime type
|
||||
occurs. If you want to use a custom mime type guesser, see [this](#custom-mime-type-guessers).
|
||||
2. **@Gedmo\Mapping\Annotation\UploadableFilePath**: This annotation is used to set which field will receive the path
|
||||
to the file. The field MUST be of type "string". Either this one or UploadableFileName annotation is REQUIRED to be set.
|
||||
3. **@Gedmo\Mapping\Annotation\UploadableFileName**: This annotation is used to set which field will receive the name
|
||||
of the file. The field MUST be of type "string". Either this one or UploadableFilePath annotation is REQUIRED to be set.
|
||||
4. **@Gedmo\Mapping\Annotation\UploadableFileMimeType**: This is an optional annotation used to set which field will
|
||||
receive the mime type of the file as its value. This field MUST be of type "string".
|
||||
5. **@Gedmo\Mapping\Annotation\UploadableFileSize**: This is an optional annotation used to set which field will
|
||||
receive the size in bytes of the file as its value. This field MUST be of type "decimal".
|
||||
|
||||
### Notes about setting the path where the files will be moved:
|
||||
|
||||
You have three choices to configure the path. You can set a default path on the listener, which will be used on every
|
||||
entity which doesn't have a path or pathMethod defined:
|
||||
|
||||
``` php
|
||||
$listener->setDefaultPath('/my/path');
|
||||
```
|
||||
|
||||
You can use the Uploadable "path" option to set the path:
|
||||
|
||||
``` php
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\Uploadable(path="/my/path")
|
||||
*/
|
||||
class File
|
||||
{
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
Or you can use the Uploadable "pathMethod" option to set the name of the method which will return the path:
|
||||
|
||||
``` php
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\Uploadable(pathMethod="getPath")
|
||||
*/
|
||||
class File
|
||||
{
|
||||
public function getPath()
|
||||
{
|
||||
return '/my/path';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Note regarding the Uploadable interface:
|
||||
|
||||
The Uploadable interface is not necessary, except in cases there
|
||||
you need to identify an entity as Uploadable. The metadata is loaded only once then
|
||||
you need to identify an entity as Uploadable. The metadata is loaded only once then
|
||||
cache is activated
|
||||
|
||||
### Minimum configuration needed:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
// If you don't set the path here, remember that you must set it on the listener!
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\Uploadable
|
||||
*/
|
||||
class File
|
||||
{
|
||||
// Other fields..
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="path", type="string")
|
||||
* @Gedmo\UploadableFilePath
|
||||
*/
|
||||
private $path;
|
||||
}
|
||||
```
|
||||
|
||||
### Example of an entity with all the configurations set:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @Gedmo\Uploadable(path="/my/path", callback="myCallbackMethod", filenameGenerator="SHA1", allowOverwrite=true, appendNumber=true)
|
||||
*/
|
||||
class File
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue(strategy="IDENTITY")
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="path", type="string")
|
||||
* @Gedmo\UploadableFilePath
|
||||
*/
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="name", type="string")
|
||||
* @Gedmo\UploadableFileName
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="mime_type", type="string")
|
||||
* @Gedmo\UploadableFileMimeType
|
||||
*/
|
||||
private $mimeType;
|
||||
|
||||
/**
|
||||
* @ORM\Column(name="size", type="decimal")
|
||||
* @Gedmo\UploadableFileSize
|
||||
*/
|
||||
private $size;
|
||||
|
||||
|
||||
public function myCallbackMethod(array $info)
|
||||
{
|
||||
// Do some stuff with the file..
|
||||
}
|
||||
|
||||
// Other methods..
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
<a name="yaml-mapping"></a>
|
||||
|
||||
## Yaml mapping example:
|
||||
|
||||
Yaml mapped Article: **/mapping/yaml/Entity.Article.dcm.yml**
|
||||
|
||||
```
|
||||
---
|
||||
Entity\File:
|
||||
type: entity
|
||||
table: files
|
||||
gedmo:
|
||||
uploadable:
|
||||
allowOverwrite: true
|
||||
appendNumber: true
|
||||
path: '/my/path'
|
||||
pathMethod: getPath
|
||||
callback: callbackMethod
|
||||
filenameGenerator: SHA1
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
path:
|
||||
type: string
|
||||
gedmo:
|
||||
- uploadableFilePath
|
||||
name:
|
||||
type: string
|
||||
gedmo:
|
||||
- uploadableFileName
|
||||
mimeType:
|
||||
type: string
|
||||
gedmo:
|
||||
- uploadableFileMimeType
|
||||
size:
|
||||
type: decimal
|
||||
gedmo:
|
||||
- uploadableFileSize
|
||||
```
|
||||
|
||||
<a name="xml-mapping"></a>
|
||||
|
||||
## Xml mapping example
|
||||
|
||||
``` xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:gedmo="http://gediminasm.org/schemas/orm/doctrine-extensions-mapping">
|
||||
|
||||
<entity name="Entity\File" table="files">
|
||||
|
||||
<id name="id" type="integer" column="id">
|
||||
<generator strategy="AUTO"/>
|
||||
</id>
|
||||
|
||||
<field name="mimeType" column="mime" type="string">
|
||||
<gedmo:uploadable-file-mime-type />
|
||||
</field>
|
||||
|
||||
<field name="size" column="size" type="decimal">
|
||||
<gedmo:uploadable-file-size />
|
||||
</field>
|
||||
|
||||
<field name="name" column="name" type="string">
|
||||
<gedmo:uploadable-file-name />
|
||||
</field>
|
||||
|
||||
<field name="path" column="path" type="string">
|
||||
<gedmo:uploadable-file-path />
|
||||
</field>
|
||||
|
||||
<gedmo:uploadable
|
||||
allow-overwrite="true"
|
||||
append-number="true"
|
||||
path="/my/path"
|
||||
path-method="getPath"
|
||||
callback="callbackMethod"
|
||||
filename-generator="SHA1" />
|
||||
|
||||
</entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
```
|
||||
|
||||
<a name="usage"></a>
|
||||
|
||||
## Usage:
|
||||
|
||||
``` php
|
||||
<?php
|
||||
// Example setting the path directly on the listener:
|
||||
|
||||
$listener->setDefaultPath('/my/app/web/upload');
|
||||
|
||||
if (isset($_FILES['images']) && is_array($_FILES['images'])) {
|
||||
foreach ($_FILES['images'] as $fileInfo) {
|
||||
$file = new File();
|
||||
|
||||
$listener->addEntityFileInfo($file, $fileInfo);
|
||||
|
||||
// You can set the file info directly with a FileInfoInterface object, like this:
|
||||
//
|
||||
// $listener->addEntityFileInfo($file, new FileInfoArray($fileInfo));
|
||||
//
|
||||
// Or create your own class which implements FileInfoInterface
|
||||
//
|
||||
// $listener->addEntityFileInfo($file, new MyOwnFileInfo($fileInfo));
|
||||
|
||||
|
||||
$em->persist($file);
|
||||
}
|
||||
}
|
||||
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
Easy like that, any suggestions on improvements are very welcome.
|
||||
|
||||
<a name="additional-usages"></a>
|
||||
|
||||
### Using the extension to handle not only uploaded files
|
||||
|
||||
Maybe you want to handle files obtained from an URL, or even files that are already located in the same server than your app.
|
||||
This can be handled in a very simple way. First, you need to create a class that implements the FileInfoInterface
|
||||
interface. As an example:
|
||||
|
||||
``` php
|
||||
use Gedmo\Uploadable\FileInfo\FileInfoInterface;
|
||||
|
||||
class CustomFileInfo implements FileInfoInterface
|
||||
{
|
||||
protected $path;
|
||||
protected $size;
|
||||
protected $type;
|
||||
protected $filename;
|
||||
protected $error = 0;
|
||||
|
||||
public function __construct($path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
||||
// Now, process the file and fill the rest of the properties.
|
||||
}
|
||||
|
||||
// This returns the actual path of the file
|
||||
public function getTmpName()
|
||||
{
|
||||
return $path;
|
||||
}
|
||||
|
||||
// This returns the filename
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
// This returns the file size in bytes
|
||||
public function getSize()
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
// This returns the mime type
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getError()
|
||||
{
|
||||
// This should return 0, as it's only used to return the codes from PHP file upload errors.
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
// If this method returns true, it will produce that the extension uses "move_uploaded_file" function to move
|
||||
// the file. If it returns false, the extension will use the "copy" function.
|
||||
public function isUploadedFile()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or you could simply extend the FileInfoArray class and do the following:
|
||||
|
||||
``` php
|
||||
use Gedmo\Uploadable\FileInfo\FileInfoArray;
|
||||
|
||||
class CustomFileInfo extends FileInfoArray
|
||||
{
|
||||
public function __construct($path)
|
||||
{
|
||||
// There's already a $fileInfo property, which needs to be an array with the
|
||||
// following keys: tmp_name, name, size, type, error
|
||||
$this->fileInfo = array(
|
||||
'tmp_name' => '',
|
||||
'name' => '',
|
||||
'size' => 0,
|
||||
'type' => '',
|
||||
'error' => 0
|
||||
);
|
||||
|
||||
// Now process the file at $path and fill the keys with the correct values.
|
||||
//
|
||||
// In this example we use a $path as the first argument, but it could be an URL
|
||||
// to the file we need to obtain, etc.
|
||||
}
|
||||
|
||||
public function isUploadedFile()
|
||||
{
|
||||
// Remember to set this to false so we use "copy" instead of "move_uploaded_file"
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And that's it. Then, instead of getting the file info from the $_FILES array, you would do:
|
||||
|
||||
``` php
|
||||
// We set the default path in the listener again
|
||||
$listener->setDefaultPath('/my/path');
|
||||
|
||||
$file = new File();
|
||||
|
||||
$listener->addEntityFileInfo($file, new CustomFileInfo('/path/to/file.txt'));
|
||||
|
||||
$em->persist($file);
|
||||
$em->flush();
|
||||
```
|
||||
|
||||
<a name="custom-mime-type-guessers"></a>
|
||||
|
||||
### Custom Mime type guessers
|
||||
|
||||
If you want to use your own mime type guesser, you need to implement the interface "Gedmo\Uploadable\MimeType\MimeTypeGuesserInterface",
|
||||
which has only one method: "guess($filePath)". Then, you can set the mime type guesser used on the listener in the following
|
||||
way:
|
||||
|
||||
``` php
|
||||
$listener->setMimeTypeGuesser(new MyCustomMimeTypeGuesser());
|
||||
|
||||
```
|
||||
97
vendor/gedmo/doctrine-extensions/doc/zendframework2.md
vendored
Normal file
97
vendor/gedmo/doctrine-extensions/doc/zendframework2.md
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
## Using Gedmo Doctrine Extensions in Zend Framework 2
|
||||
|
||||
Assuming you are familiar with [DoctrineModule](https://github.com/doctrine/DoctrineModule) (if not, you should definitely start there!), integrating Doctrine Extensions with Zend Framework 2 application is super-easy.
|
||||
|
||||
### Composer
|
||||
|
||||
Add DoctrineModule, DoctrineORMModule and DoctrineExtensions to composer.json file:
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"zendframework/zendframework": "2.1.*",
|
||||
"doctrine/doctrine-module": "0.*",
|
||||
"doctrine/doctrine-orm-module": "0.*",
|
||||
"gedmo/doctrine-extensions": "2.3.*",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then run `composer.phar update`.
|
||||
|
||||
### Configuration
|
||||
|
||||
Once libraries are installed, you can tell Doctrine which behaviors you want to use, by declaring appropriate subscribers in Event Manager settings. Together with [entity mapping options](https://github.com/doctrine/DoctrineORMModule#entities-settings), your module configuration file should look like following:
|
||||
|
||||
```php
|
||||
return array(
|
||||
'doctrine' => array(
|
||||
'eventmanager' => array(
|
||||
'orm_default' => array(
|
||||
'subscribers' => array(
|
||||
|
||||
// pick any listeners you need
|
||||
'Gedmo\Tree\TreeListener',
|
||||
'Gedmo\Timestampable\TimestampableListener',
|
||||
'Gedmo\Sluggable\SluggableListener',
|
||||
'Gedmo\Loggable\LoggableListener',
|
||||
'Gedmo\Sortable\SortableListener'
|
||||
),
|
||||
),
|
||||
),
|
||||
'driver' => array(
|
||||
'my_driver' => array(
|
||||
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
|
||||
'cache' => 'array',
|
||||
'paths' => array(__DIR__ . '/../src/MyModule/Entity')
|
||||
),
|
||||
'orm_default' => array(
|
||||
'drivers' => array(
|
||||
'MyModule\Entity' => 'my_driver'
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
```
|
||||
|
||||
That's it! From now on you can use Gedmo annotations, just as it is described in [documentation](https://github.com/mtymek/DoctrineExtensions/blob/master/doc/annotations.md).
|
||||
|
||||
#### Note: You may need to provide additional settings for some of the available listeners.
|
||||
|
||||
For instance, `Translatable` requires additional metadata driver in order to manage translation tables:
|
||||
|
||||
```php
|
||||
return array(
|
||||
'doctrine' => array(
|
||||
'eventmanager' => array(
|
||||
'orm_default' => array(
|
||||
'subscribers' => array(
|
||||
'Gedmo\Translatable\TranslatableListener',
|
||||
),
|
||||
),
|
||||
),
|
||||
'driver' => array(
|
||||
'my_driver' => array(
|
||||
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
|
||||
'cache' => 'array',
|
||||
'paths' => array(__DIR__ . '/../src/MyModule/Entity')
|
||||
),
|
||||
'translatable_metadata_driver' => array(
|
||||
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
|
||||
'cache' => 'array',
|
||||
'paths' => array(
|
||||
'vendor/gedmo/doctrine-extensions/lib/Gedmo/Translatable/Entity',
|
||||
),
|
||||
),
|
||||
'orm_default' => array(
|
||||
'drivers' => array(
|
||||
'MyModule\Entity' => 'my_driver',
|
||||
'Gedmo\Translatable\Entity' => 'translatable_metadata_driver',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
```
|
||||
219
vendor/gedmo/doctrine-extensions/example/app/Entity/Category.php
vendored
Normal file
219
vendor/gedmo/doctrine-extensions/example/app/Entity/Category.php
vendored
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @Gedmo\Tree(type="nested")
|
||||
* @ORM\Table(name="ext_categories")
|
||||
* @ORM\Entity(repositoryClass="Entity\Repository\CategoryRepository")
|
||||
* @Gedmo\TranslationEntity(class="Entity\CategoryTranslation")
|
||||
*/
|
||||
class Category
|
||||
{
|
||||
/**
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(length=64)
|
||||
*/
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @ORM\Column(type="text", nullable=true)
|
||||
*/
|
||||
private $description;
|
||||
|
||||
/**
|
||||
* @Gedmo\Translatable
|
||||
* @Gedmo\Slug(fields={"created", "title"})
|
||||
* @ORM\Column(length=64, unique=true)
|
||||
*/
|
||||
private $slug;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeLeft
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $lft;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeRight
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
private $rgt;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeParent
|
||||
* @ORM\ManyToOne(targetEntity="Category", inversedBy="children")
|
||||
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeRoot
|
||||
* @ORM\Column(type="integer", nullable=true)
|
||||
*/
|
||||
private $root;
|
||||
|
||||
/**
|
||||
* @Gedmo\TreeLevel
|
||||
* @ORM\Column(name="lvl", type="integer")
|
||||
*/
|
||||
private $level;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(targetEntity="Category", mappedBy="parent")
|
||||
*/
|
||||
private $children;
|
||||
|
||||
/**
|
||||
* @Gedmo\Timestampable(on="create")
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private $created;
|
||||
|
||||
/**
|
||||
* @Gedmo\Timestampable(on="update")
|
||||
* @ORM\Column(type="datetime")
|
||||
*/
|
||||
private $updated;
|
||||
|
||||
/**
|
||||
* @Gedmo\Blameable(on="create")
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private $createdBy;
|
||||
|
||||
/**
|
||||
* @Gedmo\Blameable(on="update")
|
||||
* @ORM\Column(type="string")
|
||||
*/
|
||||
private $updatedBy;
|
||||
|
||||
/**
|
||||
* @ORM\OneToMany(
|
||||
* targetEntity="CategoryTranslation",
|
||||
* mappedBy="object",
|
||||
* cascade={"persist", "remove"}
|
||||
* )
|
||||
*/
|
||||
private $translations;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->children = new ArrayCollection();
|
||||
$this->translations = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getTranslations()
|
||||
{
|
||||
return $this->translations;
|
||||
}
|
||||
|
||||
public function addTranslation(CategoryTranslation $t)
|
||||
{
|
||||
if (!$this->translations->contains($t)) {
|
||||
$this->translations[] = $t;
|
||||
$t->setObject($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSlug()
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setTitle($title)
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setDescription($description)
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setParent($parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function getRoot()
|
||||
{
|
||||
return $this->root;
|
||||
}
|
||||
|
||||
public function getLevel()
|
||||
{
|
||||
return $this->level;
|
||||
}
|
||||
|
||||
public function getChildren()
|
||||
{
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
public function getLeft()
|
||||
{
|
||||
return $this->lft;
|
||||
}
|
||||
|
||||
public function getRight()
|
||||
{
|
||||
return $this->rgt;
|
||||
}
|
||||
|
||||
public function getCreated()
|
||||
{
|
||||
return $this->created;
|
||||
}
|
||||
|
||||
public function getUpdated()
|
||||
{
|
||||
return $this->updated;
|
||||
}
|
||||
|
||||
public function getCreatedBy()
|
||||
{
|
||||
return $this->createdBy;
|
||||
}
|
||||
|
||||
public function getUpdatedBy()
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getTitle();
|
||||
}
|
||||
}
|
||||
37
vendor/gedmo/doctrine-extensions/example/app/Entity/CategoryTranslation.php
vendored
Normal file
37
vendor/gedmo/doctrine-extensions/example/app/Entity/CategoryTranslation.php
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation;
|
||||
|
||||
/**
|
||||
* @ORM\Entity
|
||||
* @ORM\Table(name="category_translations",
|
||||
* uniqueConstraints={@ORM\UniqueConstraint(name="lookup_unique_idx", columns={
|
||||
* "locale", "object_id", "field"
|
||||
* })}
|
||||
* )
|
||||
*/
|
||||
class CategoryTranslation extends AbstractPersonalTranslation
|
||||
{
|
||||
/**
|
||||
* Convinient constructor
|
||||
*
|
||||
* @param string $locale
|
||||
* @param string $field
|
||||
* @param string $value
|
||||
*/
|
||||
public function __construct($locale, $field, $value)
|
||||
{
|
||||
$this->setLocale($locale);
|
||||
$this->setField($field);
|
||||
$this->setContent($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ORM\ManyToOne(targetEntity="Category", inversedBy="translations")
|
||||
* @ORM\JoinColumn(name="object_id", referencedColumnName="id", onDelete="CASCADE")
|
||||
*/
|
||||
protected $object;
|
||||
}
|
||||
9
vendor/gedmo/doctrine-extensions/example/app/Entity/Repository/CategoryRepository.php
vendored
Normal file
9
vendor/gedmo/doctrine-extensions/example/app/Entity/Repository/CategoryRepository.php
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Entity\Repository;
|
||||
|
||||
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
|
||||
|
||||
class CategoryRepository extends NestedTreeRepository
|
||||
{
|
||||
}
|
||||
6
vendor/gedmo/doctrine-extensions/example/bin/console
vendored
Executable file
6
vendor/gedmo/doctrine-extensions/example/bin/console
vendored
Executable file
|
|
@ -0,0 +1,6 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$cli = include __DIR__.'/console.php';
|
||||
$cli->run();
|
||||
|
||||
38
vendor/gedmo/doctrine-extensions/example/bin/console.php
vendored
Normal file
38
vendor/gedmo/doctrine-extensions/example/bin/console.php
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
$em = include __DIR__.'/../em.php';
|
||||
|
||||
$cli = new Symfony\Component\Console\Application('My CLI interface', '1.0.0');
|
||||
$cli->setCatchExceptions(true);
|
||||
// commands
|
||||
$cli->addCommands(array(
|
||||
// DBAL Commands
|
||||
new Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(),
|
||||
new Doctrine\DBAL\Tools\Console\Command\ImportCommand(),
|
||||
|
||||
// ORM Commands
|
||||
new Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\RunDqlCommand(),
|
||||
new Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(),
|
||||
));
|
||||
// helpers
|
||||
$helpers = array(
|
||||
'db' => new Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
|
||||
'em' => new Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em),
|
||||
);
|
||||
foreach ($helpers as $name => $helper) {
|
||||
$cli->getHelperSet()->set($helper, $name);
|
||||
}
|
||||
|
||||
return $cli;
|
||||
122
vendor/gedmo/doctrine-extensions/example/em.php
vendored
Normal file
122
vendor/gedmo/doctrine-extensions/example/em.php
vendored
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
/**
|
||||
* This entity manager configuration works with doctrine 2.1.x and 2.2.x
|
||||
* versions. Regarding AnnotationDriver setup it most probably will be changed into
|
||||
* xml. Because annotation driver fails to read other classes in same namespace
|
||||
*/
|
||||
// connection args, modify at will
|
||||
$connection = array(
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 3306,
|
||||
'user' => 'root',
|
||||
'password' => null,
|
||||
'dbname' => 'test',
|
||||
'driver' => 'pdo_mysql',
|
||||
);
|
||||
if (!file_exists(__DIR__.'/../vendor/autoload.php')) {
|
||||
die('cannot find vendors, read README.md how to use composer');
|
||||
}
|
||||
// First of all autoloading of vendors
|
||||
$loader = require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
// gedmo extensions
|
||||
$loader->add('Gedmo', __DIR__.'/../lib');
|
||||
|
||||
// autoloader for Entity namespace
|
||||
$loader->add('Entity', __DIR__.'/app');
|
||||
|
||||
// ensure standard doctrine annotations are registered
|
||||
Doctrine\Common\Annotations\AnnotationRegistry::registerFile(
|
||||
__DIR__.'/../vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php'
|
||||
);
|
||||
|
||||
// Second configure ORM
|
||||
// globally used cache driver, in production use APC or memcached
|
||||
$cache = new Doctrine\Common\Cache\ArrayCache();
|
||||
// standard annotation reader
|
||||
$annotationReader = new Doctrine\Common\Annotations\AnnotationReader();
|
||||
$cachedAnnotationReader = new Doctrine\Common\Annotations\CachedReader(
|
||||
$annotationReader, // use reader
|
||||
$cache // and a cache driver
|
||||
);
|
||||
// create a driver chain for metadata reading
|
||||
$driverChain = new Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain();
|
||||
// load superclass metadata mapping only, into driver chain
|
||||
// also registers Gedmo annotations.NOTE: you can personalize it
|
||||
Gedmo\DoctrineExtensions::registerAbstractMappingIntoDriverChainORM(
|
||||
$driverChain, // our metadata driver chain, to hook into
|
||||
$cachedAnnotationReader // our cached annotation reader
|
||||
);
|
||||
|
||||
// now we want to register our application entities,
|
||||
// for that we need another metadata driver used for Entity namespace
|
||||
$annotationDriver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver(
|
||||
$cachedAnnotationReader, // our cached annotation reader
|
||||
array(__DIR__.'/app/Entity') // paths to look in
|
||||
);
|
||||
// NOTE: driver for application Entity can be different, Yaml, Xml or whatever
|
||||
// register annotation driver for our application Entity fully qualified namespace
|
||||
$driverChain->addDriver($annotationDriver, 'Entity');
|
||||
|
||||
// general ORM configuration
|
||||
$config = new Doctrine\ORM\Configuration();
|
||||
$config->setProxyDir(sys_get_temp_dir());
|
||||
$config->setProxyNamespace('Proxy');
|
||||
$config->setAutoGenerateProxyClasses(false); // this can be based on production config.
|
||||
// register metadata driver
|
||||
$config->setMetadataDriverImpl($driverChain);
|
||||
// use our allready initialized cache driver
|
||||
$config->setMetadataCacheImpl($cache);
|
||||
$config->setQueryCacheImpl($cache);
|
||||
|
||||
// Third, create event manager and hook prefered extension listeners
|
||||
$evm = new Doctrine\Common\EventManager();
|
||||
// gedmo extension listeners
|
||||
|
||||
// sluggable
|
||||
$sluggableListener = new Gedmo\Sluggable\SluggableListener();
|
||||
// you should set the used annotation reader to listener, to avoid creating new one for mapping drivers
|
||||
$sluggableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($sluggableListener);
|
||||
|
||||
// tree
|
||||
$treeListener = new Gedmo\Tree\TreeListener();
|
||||
$treeListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($treeListener);
|
||||
|
||||
// loggable, not used in example
|
||||
//$loggableListener = new Gedmo\Loggable\LoggableListener;
|
||||
//$loggableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
//$loggableListener->setUsername('admin');
|
||||
//$evm->addEventSubscriber($loggableListener);
|
||||
|
||||
// timestampable
|
||||
$timestampableListener = new Gedmo\Timestampable\TimestampableListener();
|
||||
$timestampableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($timestampableListener);
|
||||
|
||||
// blameable
|
||||
|
||||
$blameableListener = new \Gedmo\Blameable\BlameableListener();
|
||||
$blameableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$blameableListener->setUserValue('MyUsername'); // determine from your environment
|
||||
$evm->addEventSubscriber($blameableListener);
|
||||
|
||||
// translatable
|
||||
$translatableListener = new Gedmo\Translatable\TranslatableListener();
|
||||
// current translation locale should be set from session or hook later into the listener
|
||||
// most important, before entity manager is flushed
|
||||
$translatableListener->setTranslatableLocale('en');
|
||||
$translatableListener->setDefaultLocale('en');
|
||||
$translatableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
$evm->addEventSubscriber($translatableListener);
|
||||
|
||||
// sortable, not used in example
|
||||
//$sortableListener = new Gedmo\Sortable\SortableListener;
|
||||
//$sortableListener->setAnnotationReader($cachedAnnotationReader);
|
||||
//$evm->addEventSubscriber($sortableListener);
|
||||
|
||||
// mysql set names UTF-8 if required
|
||||
$evm->addEventSubscriber(new Doctrine\DBAL\Event\Listeners\MysqlSessionInit());
|
||||
// Finally, create entity manager
|
||||
return Doctrine\ORM\EntityManager::create($connection, $config, $evm);
|
||||
79
vendor/gedmo/doctrine-extensions/example/run.php
vendored
Normal file
79
vendor/gedmo/doctrine-extensions/example/run.php
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
use Gedmo\Translatable\TranslatableListener;
|
||||
|
||||
$executionStart = microtime(true);
|
||||
$memoryStart = memory_get_usage(true);
|
||||
|
||||
$em = include 'em.php';
|
||||
|
||||
/**
|
||||
* initialized in em.php
|
||||
*
|
||||
* Gedmo\Translatable\TranslationListener
|
||||
*/
|
||||
|
||||
$repository = $em->getRepository('Entity\Category');
|
||||
$food = $repository->findOneByTitle('Food');
|
||||
if (!$food) {
|
||||
// lets create some categories
|
||||
$food = new Entity\Category();
|
||||
$food->setTitle('Food');
|
||||
$food->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Maistas'));
|
||||
|
||||
$fruits = new Entity\Category();
|
||||
$fruits->setParent($food);
|
||||
$fruits->setTitle('Fruits');
|
||||
$fruits->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Vaisiai'));
|
||||
|
||||
$apple = new Entity\Category();
|
||||
$apple->setParent($fruits);
|
||||
$apple->setTitle('Apple');
|
||||
$apple->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Obuolys'));
|
||||
|
||||
$milk = new Entity\Category();
|
||||
$milk->setParent($food);
|
||||
$milk->setTitle('Milk');
|
||||
$milk->addTranslation(new Entity\CategoryTranslation('lt', 'title', 'Pienas'));
|
||||
|
||||
$em->persist($food);
|
||||
$em->persist($milk);
|
||||
$em->persist($fruits);
|
||||
$em->persist($apple);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
// create query to fetch tree nodes
|
||||
$query = $em
|
||||
->createQueryBuilder()
|
||||
->select('node')
|
||||
->from('Entity\Category', 'node')
|
||||
->orderBy('node.root, node.lft', 'ASC')
|
||||
->getQuery()
|
||||
;
|
||||
// set hint to translate nodes
|
||||
$query->setHint(
|
||||
Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER,
|
||||
'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker'
|
||||
);
|
||||
$treeDecorationOptions = array(
|
||||
'decorate' => true,
|
||||
'rootOpen' => '',
|
||||
'rootClose' => '',
|
||||
'childOpen' => '',
|
||||
'childClose' => '',
|
||||
'nodeDecorator' => function ($node) {
|
||||
return str_repeat('-', $node['level']).$node['title'].PHP_EOL;
|
||||
},
|
||||
);
|
||||
// build tree in english
|
||||
echo $repository->buildTree($query->getArrayResult(), $treeDecorationOptions).PHP_EOL.PHP_EOL;
|
||||
// change locale
|
||||
$query->setHint(TranslatableListener::HINT_TRANSLATABLE_LOCALE, 'lt');
|
||||
// build tree in lithuanian
|
||||
echo $repository->buildTree($query->getArrayResult(), $treeDecorationOptions).PHP_EOL.PHP_EOL;
|
||||
|
||||
$ms = round(microtime(true) - $executionStart, 4) * 1000;
|
||||
$mem = round((memory_get_usage(true) - $memoryStart) / 1000000, 2);
|
||||
echo "Execution took: {$ms} ms, memory consumed: {$mem} Mb";
|
||||
227
vendor/gedmo/doctrine-extensions/lib/Gedmo/AbstractTrackingListener.php
vendored
Normal file
227
vendor/gedmo/doctrine-extensions/lib/Gedmo/AbstractTrackingListener.php
vendored
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\Common\NotifyPropertyChanged;
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
use Gedmo\Exception\UnexpectedValueException;
|
||||
use Gedmo\Mapping\Event\AdapterInterface;
|
||||
use Gedmo\Mapping\MappedEventSubscriber;
|
||||
|
||||
/**
|
||||
* The Timestampable listener handles the update of
|
||||
* dates on creation and update.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
abstract class AbstractTrackingListener extends MappedEventSubscriber
|
||||
{
|
||||
/**
|
||||
* Specifies the list of events to listen
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'prePersist',
|
||||
'onFlush',
|
||||
'loadClassMetadata',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps additional metadata for the Entity
|
||||
*
|
||||
* @param EventArgs $eventArgs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadClassMetadata(EventArgs $eventArgs)
|
||||
{
|
||||
$ea = $this->getEventAdapter($eventArgs);
|
||||
$this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for Timestampable objects being updated
|
||||
* to update modification date
|
||||
*
|
||||
* @param EventArgs $args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onFlush(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
$om = $ea->getObjectManager();
|
||||
$uow = $om->getUnitOfWork();
|
||||
// check all scheduled updates
|
||||
$all = array_merge($ea->getScheduledObjectInsertions($uow), $ea->getScheduledObjectUpdates($uow));
|
||||
foreach ($all as $object) {
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
if (!$config = $this->getConfiguration($om, $meta->name)) {
|
||||
continue;
|
||||
}
|
||||
$changeSet = $ea->getObjectChangeSet($uow, $object);
|
||||
$needChanges = false;
|
||||
|
||||
if ($uow->isScheduledForInsert($object) && isset($config['create'])) {
|
||||
foreach ($config['create'] as $field) {
|
||||
// Field can not exist in change set, when persisting embedded document without parent for example
|
||||
$new = array_key_exists($field, $changeSet) ? $changeSet[$field][1] : false;
|
||||
if ($new === null) { // let manual values
|
||||
$needChanges = true;
|
||||
$this->updateField($object, $ea, $meta, $field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['update'])) {
|
||||
foreach ($config['update'] as $field) {
|
||||
$isInsertAndNull = $uow->isScheduledForInsert($object)
|
||||
&& array_key_exists($field, $changeSet)
|
||||
&& $changeSet[$field][1] === null;
|
||||
if (!isset($changeSet[$field]) || $isInsertAndNull) { // let manual values
|
||||
$needChanges = true;
|
||||
$this->updateField($object, $ea, $meta, $field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$uow->isScheduledForInsert($object) && isset($config['change'])) {
|
||||
foreach ($config['change'] as $options) {
|
||||
if (isset($changeSet[$options['field']])) {
|
||||
continue; // value was set manually
|
||||
}
|
||||
|
||||
if (!is_array($options['trackedField'])) {
|
||||
$singleField = true;
|
||||
$trackedFields = array($options['trackedField']);
|
||||
} else {
|
||||
$singleField = false;
|
||||
$trackedFields = $options['trackedField'];
|
||||
}
|
||||
|
||||
foreach ($trackedFields as $trackedField) {
|
||||
$trackedChild = null;
|
||||
$tracked = null;
|
||||
$parts = explode('.', $trackedField);
|
||||
if (isset($parts[1])) {
|
||||
$tracked = $parts[0];
|
||||
$trackedChild = $parts[1];
|
||||
}
|
||||
|
||||
if (!isset($tracked) || array_key_exists($trackedField, $changeSet)) {
|
||||
$tracked = $trackedField;
|
||||
$trackedChild = null;
|
||||
}
|
||||
|
||||
if (isset($changeSet[$tracked])) {
|
||||
$changes = $changeSet[$tracked];
|
||||
if (isset($trackedChild)) {
|
||||
$changingObject = $changes[1];
|
||||
if (!is_object($changingObject)) {
|
||||
throw new UnexpectedValueException(
|
||||
"Field - [{$tracked}] is expected to be object in class - {$meta->name}"
|
||||
);
|
||||
}
|
||||
$objectMeta = $om->getClassMetadata(get_class($changingObject));
|
||||
$om->initializeObject($changingObject);
|
||||
$value = $objectMeta->getReflectionProperty($trackedChild)->getValue($changingObject);
|
||||
} else {
|
||||
$value = $changes[1];
|
||||
}
|
||||
|
||||
if (($singleField && in_array($value, (array) $options['value'])) || $options['value'] === null) {
|
||||
$needChanges = true;
|
||||
$this->updateField($object, $ea, $meta, $options['field']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($needChanges) {
|
||||
$ea->recomputeSingleObjectChangeSet($uow, $meta, $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for persisted Timestampable objects
|
||||
* to update creation and modification dates
|
||||
*
|
||||
* @param EventArgs $args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prePersist(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
$om = $ea->getObjectManager();
|
||||
$object = $ea->getObject();
|
||||
$meta = $om->getClassMetadata(get_class($object));
|
||||
if ($config = $this->getConfiguration($om, $meta->getName())) {
|
||||
if (isset($config['update'])) {
|
||||
foreach ($config['update'] as $field) {
|
||||
if ($meta->getReflectionProperty($field)->getValue($object) === null) { // let manual values
|
||||
$this->updateField($object, $ea, $meta, $field);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($config['create'])) {
|
||||
foreach ($config['create'] as $field) {
|
||||
if ($meta->getReflectionProperty($field)->getValue($object) === null) { // let manual values
|
||||
$this->updateField($object, $ea, $meta, $field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value for update field
|
||||
*
|
||||
* @param ClassMetadata $meta
|
||||
* @param string $field
|
||||
* @param AdapterInterface $eventAdapter
|
||||
*/
|
||||
abstract protected function getFieldValue($meta, $field, $eventAdapter);
|
||||
|
||||
/**
|
||||
* Updates a field
|
||||
*
|
||||
* @param object $object
|
||||
* @param AdapterInterface $eventAdapter
|
||||
* @param ClassMetadata $meta
|
||||
* @param string $field
|
||||
*/
|
||||
protected function updateField($object, $eventAdapter, $meta, $field)
|
||||
{
|
||||
/** @var \Doctrine\Orm\Mapping\ClassMetadata|\Doctrine\ODM\MongoDB\Mapping\ClassMetadata $meta */
|
||||
$property = $meta->getReflectionProperty($field);
|
||||
$oldValue = $property->getValue($object);
|
||||
$newValue = $this->getFieldValue($meta, $field, $eventAdapter);
|
||||
|
||||
// if field value is reference, persist object
|
||||
if ($meta->hasAssociation($field) && is_object($newValue) && !$eventAdapter->getObjectManager()->contains($newValue)) {
|
||||
$uow = $eventAdapter->getObjectManager()->getUnitOfWork();
|
||||
|
||||
// Check to persist only when the entity isn't already managed, persists always for MongoDB
|
||||
if(!($uow instanceof UnitOfWork) || $uow->getEntityState($newValue) !== UnitOfWork::STATE_MANAGED) {
|
||||
$eventAdapter->getObjectManager()->persist($newValue);
|
||||
}
|
||||
}
|
||||
|
||||
$property->setValue($object, $newValue);
|
||||
|
||||
if ($object instanceof NotifyPropertyChanged) {
|
||||
$uow = $eventAdapter->getObjectManager()->getUnitOfWork();
|
||||
$uow->propertyChanged($object, $field, $oldValue, $newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
50
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Blameable.php
vendored
Normal file
50
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Blameable.php
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable;
|
||||
|
||||
/**
|
||||
* This interface is not necessary but can be implemented for
|
||||
* Entities which in some cases needs to be identified as
|
||||
* Blameable
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface Blameable
|
||||
{
|
||||
// blameable expects annotations on properties
|
||||
|
||||
/**
|
||||
* @gedmo:Blameable(on="create")
|
||||
* fields which should be updated on insert only
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:Blameable(on="update")
|
||||
* fields which should be updated on update and insert
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:Blameable(on="change", field="field", value="value")
|
||||
* fields which should be updated on changed "property"
|
||||
* value and become equal to given "value"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:Blameable(on="change", field="field")
|
||||
* fields which should be updated on changed "property"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:Blameable(on="change", fields={"field1", "field2"})
|
||||
* fields which should be updated if at least one of the given fields changed
|
||||
*/
|
||||
|
||||
/**
|
||||
* example
|
||||
*
|
||||
* @gedmo:Blameable(on="create")
|
||||
* @Column(type="string")
|
||||
* $created
|
||||
*/
|
||||
}
|
||||
72
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/BlameableListener.php
vendored
Normal file
72
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/BlameableListener.php
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable;
|
||||
|
||||
use Doctrine\Common\NotifyPropertyChanged;
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use Gedmo\AbstractTrackingListener;
|
||||
use Gedmo\Exception\InvalidArgumentException;
|
||||
use Gedmo\Timestampable\TimestampableListener;
|
||||
use Gedmo\Blameable\Mapping\Event\BlameableAdapter;
|
||||
|
||||
/**
|
||||
* The Blameable listener handles the update of
|
||||
* dates on creation and update.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class BlameableListener extends AbstractTrackingListener
|
||||
{
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Get the user value to set on a blameable field
|
||||
*
|
||||
* @param object $meta
|
||||
* @param string $field
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getFieldValue($meta, $field, $eventAdapter)
|
||||
{
|
||||
if ($meta->hasAssociation($field)) {
|
||||
if (null !== $this->user && ! is_object($this->user)) {
|
||||
throw new InvalidArgumentException("Blame is reference, user must be an object");
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
// ok so its not an association, then it is a string
|
||||
if (is_object($this->user)) {
|
||||
if (method_exists($this->user, 'getUsername')) {
|
||||
return (string) $this->user->getUsername();
|
||||
}
|
||||
if (method_exists($this->user, '__toString')) {
|
||||
return $this->user->__toString();
|
||||
}
|
||||
throw new InvalidArgumentException("Field expects string, user must be a string, or object should have method getUsername or __toString");
|
||||
}
|
||||
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user value to return
|
||||
*
|
||||
* @param mixed $user
|
||||
*/
|
||||
public function setUserValue($user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getNamespace()
|
||||
{
|
||||
return __NAMESPACE__;
|
||||
}
|
||||
}
|
||||
86
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Annotation.php
vendored
Normal file
86
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Annotation.php
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\AbstractAnnotationDriver;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is an annotation mapping driver for Blameable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from Annotations specifically for Blameable
|
||||
* extension.
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Annotation extends AbstractAnnotationDriver
|
||||
{
|
||||
/**
|
||||
* Annotation field is blameable
|
||||
*/
|
||||
const BLAMEABLE = 'Gedmo\\Mapping\\Annotation\\Blameable';
|
||||
|
||||
/**
|
||||
* List of types which are valid for blame
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $validTypes = array(
|
||||
'one',
|
||||
'string',
|
||||
'int',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
$class = $this->getMetaReflectionClass($meta);
|
||||
// property annotations
|
||||
foreach ($class->getProperties() as $property) {
|
||||
if ($meta->isMappedSuperclass && !$property->isPrivate() ||
|
||||
$meta->isInheritedField($property->name) ||
|
||||
isset($meta->associationMappings[$property->name]['inherited'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if ($blameable = $this->reader->getPropertyAnnotation($property, self::BLAMEABLE)) {
|
||||
$field = $property->getName();
|
||||
|
||||
if (!$meta->hasField($field) && !$meta->hasAssociation($field)) {
|
||||
throw new InvalidMappingException("Unable to find blameable [{$field}] as mapped property in entity - {$meta->name}");
|
||||
}
|
||||
if ($meta->hasField($field)) {
|
||||
if ( !$this->isValidField($meta, $field)) {
|
||||
throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' or a one-to-many relation in class - {$meta->name}");
|
||||
}
|
||||
} else {
|
||||
// association
|
||||
if (! $meta->isSingleValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}");
|
||||
}
|
||||
}
|
||||
if (!in_array($blameable->on, array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
if ($blameable->on == 'change') {
|
||||
if (!isset($blameable->field)) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
if (is_array($blameable->field) && isset($blameable->value)) {
|
||||
throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $blameable->field,
|
||||
'value' => $blameable->value,
|
||||
);
|
||||
}
|
||||
// properties are unique and mapper checks that, no risk here
|
||||
$config[$blameable->on][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Xml.php
vendored
Normal file
129
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Xml.php
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\Xml as BaseXml;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is a xml mapping driver for Blameable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from xml specifically for Blameable
|
||||
* extension.
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Xml extends BaseXml
|
||||
{
|
||||
/**
|
||||
* List of types which are valid for blame
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $validTypes = array(
|
||||
'one',
|
||||
'string',
|
||||
'int',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
/**
|
||||
* @var \SimpleXmlElement $mapping
|
||||
*/
|
||||
$mapping = $this->_getMapping($meta->name);
|
||||
|
||||
if (isset($mapping->field)) {
|
||||
/**
|
||||
* @var \SimpleXmlElement $fieldMapping
|
||||
*/
|
||||
foreach ($mapping->field as $fieldMapping) {
|
||||
$fieldMappingDoctrine = $fieldMapping;
|
||||
$fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI);
|
||||
if (isset($fieldMapping->blameable)) {
|
||||
/**
|
||||
* @var \SimpleXmlElement $data
|
||||
*/
|
||||
$data = $fieldMapping->blameable;
|
||||
|
||||
$field = $this->_getAttribute($fieldMappingDoctrine, 'name');
|
||||
if (!$this->isValidField($meta, $field)) {
|
||||
throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' or a reference in class - {$meta->name}");
|
||||
}
|
||||
if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
|
||||
if ($this->_getAttribute($data, 'on') == 'change') {
|
||||
if (!$this->_isAttributeSet($data, 'field')) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
$trackedFieldAttribute = $this->_getAttribute($data, 'field');
|
||||
$valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null;
|
||||
if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
|
||||
throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $trackedFieldAttribute,
|
||||
'value' => $valueAttribute,
|
||||
);
|
||||
}
|
||||
$config[$this->_getAttribute($data, 'on')][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping->{'many-to-one'})) {
|
||||
foreach ($mapping->{'many-to-one'} as $fieldMapping) {
|
||||
$field = $this->_getAttribute($fieldMapping, 'field');
|
||||
$fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI);
|
||||
if (isset($fieldMapping->blameable)) {
|
||||
$data = $fieldMapping->blameable;
|
||||
if (! $meta->isSingleValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}");
|
||||
}
|
||||
if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
|
||||
if ($this->_getAttribute($data, 'on') == 'change') {
|
||||
if (!$this->_isAttributeSet($data, 'field')) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
$trackedFieldAttribute = $this->_getAttribute($data, 'field');
|
||||
$valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null;
|
||||
if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
|
||||
throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $trackedFieldAttribute,
|
||||
'value' => $valueAttribute,
|
||||
);
|
||||
}
|
||||
$config[$this->_getAttribute($data, 'on')][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if $field type is valid
|
||||
*
|
||||
* @param object $meta
|
||||
* @param string $field
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isValidField($meta, $field)
|
||||
{
|
||||
$mapping = $meta->getFieldMapping($field);
|
||||
|
||||
return $mapping && in_array($mapping['type'], $this->validTypes);
|
||||
}
|
||||
}
|
||||
129
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Yaml.php
vendored
Normal file
129
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Driver/Yaml.php
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\File;
|
||||
use Gedmo\Mapping\Driver;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is a yaml mapping driver for Blameable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from yaml specifically for Blameable
|
||||
* extension.
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Yaml extends File implements Driver
|
||||
{
|
||||
/**
|
||||
* File extension
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.dcm.yml';
|
||||
|
||||
/**
|
||||
* List of types which are valid for blameable
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $validTypes = array(
|
||||
'one',
|
||||
'string',
|
||||
'int',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
$mapping = $this->_getMapping($meta->name);
|
||||
|
||||
if (isset($mapping['fields'])) {
|
||||
foreach ($mapping['fields'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo']['blameable'])) {
|
||||
$mappingProperty = $fieldMapping['gedmo']['blameable'];
|
||||
if (!$this->isValidField($meta, $field)) {
|
||||
throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' or a reference in class - {$meta->name}");
|
||||
}
|
||||
if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
|
||||
if ($mappingProperty['on'] == 'change') {
|
||||
if (!isset($mappingProperty['field'])) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
$trackedFieldAttribute = $mappingProperty['field'];
|
||||
$valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null;
|
||||
if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
|
||||
throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $trackedFieldAttribute,
|
||||
'value' => $valueAttribute,
|
||||
);
|
||||
}
|
||||
$config[$mappingProperty['on']][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['manyToOne'])) {
|
||||
foreach ($mapping['manyToOne'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo']['blameable'])) {
|
||||
$mappingProperty = $fieldMapping['gedmo']['blameable'];
|
||||
if (! $meta->isSingleValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}");
|
||||
}
|
||||
if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
|
||||
if ($mappingProperty['on'] == 'change') {
|
||||
if (!isset($mappingProperty['field'])) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
$trackedFieldAttribute = $mappingProperty['field'];
|
||||
$valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null;
|
||||
if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
|
||||
throw new InvalidMappingException("Blameable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $trackedFieldAttribute,
|
||||
'value' => $valueAttribute,
|
||||
);
|
||||
}
|
||||
$config[$mappingProperty['on']][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _loadMappingFile($file)
|
||||
{
|
||||
return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if $field type is valid
|
||||
*
|
||||
* @param \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $meta
|
||||
* @param string $field
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isValidField($meta, $field)
|
||||
{
|
||||
$mapping = $meta->getFieldMapping($field);
|
||||
|
||||
return $mapping && in_array($mapping['type'], $this->validTypes);
|
||||
}
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ODM.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ODM.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ODM as BaseAdapterODM;
|
||||
use Gedmo\Blameable\Mapping\Event\BlameableAdapter;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter for ODM adapted
|
||||
* for Blameable behavior.
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class ODM extends BaseAdapterODM implements BlameableAdapter
|
||||
{
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ORM.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/Adapter/ORM.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
|
||||
use Gedmo\Blameable\Mapping\Event\BlameableAdapter;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter for ORM adapted
|
||||
* for Blameable behavior.
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class ORM extends BaseAdapterORM implements BlameableAdapter
|
||||
{
|
||||
}
|
||||
16
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/BlameableAdapter.php
vendored
Normal file
16
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Mapping/Event/BlameableAdapter.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Mapping\Event;
|
||||
|
||||
use Gedmo\Mapping\Event\AdapterInterface;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter interface
|
||||
* for Blameable behavior.
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface BlameableAdapter extends AdapterInterface
|
||||
{
|
||||
}
|
||||
68
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/Blameable.php
vendored
Normal file
68
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/Blameable.php
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Traits;
|
||||
|
||||
/**
|
||||
* Blameable Trait, usable with PHP >= 5.4
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
trait Blameable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $createdBy;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $updatedBy;
|
||||
|
||||
/**
|
||||
* Sets createdBy.
|
||||
*
|
||||
* @param string $createdBy
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreatedBy($createdBy)
|
||||
{
|
||||
$this->createdBy = $createdBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns createdBy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedBy()
|
||||
{
|
||||
return $this->createdBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets updatedBy.
|
||||
*
|
||||
* @param string $updatedBy
|
||||
* @return $this
|
||||
*/
|
||||
public function setUpdatedBy($updatedBy)
|
||||
{
|
||||
$this->updatedBy = $updatedBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns updatedBy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdatedBy()
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
}
|
||||
75
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableDocument.php
vendored
Normal file
75
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableDocument.php
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Traits;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* Blameable Trait, usable with PHP >= 5.4
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
trait BlameableDocument
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Gedmo\Blameable(on="create")
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
protected $createdBy;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @Gedmo\Blameable(on="update")
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
protected $updatedBy;
|
||||
|
||||
/**
|
||||
* Sets createdBy.
|
||||
*
|
||||
* @param string $createdBy
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreatedBy($createdBy)
|
||||
{
|
||||
$this->createdBy = $createdBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns createdBy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedBy()
|
||||
{
|
||||
return $this->createdBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets updatedBy.
|
||||
*
|
||||
* @param string $updatedBy
|
||||
* @return $this
|
||||
*/
|
||||
public function setUpdatedBy($updatedBy)
|
||||
{
|
||||
$this->updatedBy = $updatedBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns updatedBy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdatedBy()
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
}
|
||||
75
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableEntity.php
vendored
Normal file
75
vendor/gedmo/doctrine-extensions/lib/Gedmo/Blameable/Traits/BlameableEntity.php
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Blameable\Traits;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* Blameable Trait, usable with PHP >= 5.4
|
||||
*
|
||||
* @author David Buchmann <mail@davidbu.ch>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
trait BlameableEntity
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Gedmo\Blameable(on="create")
|
||||
* @ORM\Column(nullable=true)
|
||||
*/
|
||||
protected $createdBy;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @Gedmo\Blameable(on="update")
|
||||
* @ORM\Column(nullable=true)
|
||||
*/
|
||||
protected $updatedBy;
|
||||
|
||||
/**
|
||||
* Sets createdBy.
|
||||
*
|
||||
* @param string $createdBy
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreatedBy($createdBy)
|
||||
{
|
||||
$this->createdBy = $createdBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns createdBy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedBy()
|
||||
{
|
||||
return $this->createdBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets updatedBy.
|
||||
*
|
||||
* @param string $updatedBy
|
||||
* @return $this
|
||||
*/
|
||||
public function setUpdatedBy($updatedBy)
|
||||
{
|
||||
$this->updatedBy = $updatedBy;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns updatedBy.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdatedBy()
|
||||
{
|
||||
return $this->updatedBy;
|
||||
}
|
||||
}
|
||||
117
vendor/gedmo/doctrine-extensions/lib/Gedmo/DoctrineExtensions.php
vendored
Normal file
117
vendor/gedmo/doctrine-extensions/lib/Gedmo/DoctrineExtensions.php
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
use Doctrine\ORM\Mapping\Driver as DriverORM;
|
||||
use Doctrine\ODM\MongoDB\Mapping\Driver as DriverMongodbODM;
|
||||
use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
|
||||
use Doctrine\Common\Annotations\Reader;
|
||||
use Doctrine\Common\Annotations\CachedReader;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
|
||||
/**
|
||||
* Version class allows to checking the dependencies required
|
||||
* and the current version of doctrine extensions
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class DoctrineExtensions
|
||||
{
|
||||
/**
|
||||
* Current version of extensions
|
||||
*/
|
||||
const VERSION = 'v2.4.26';
|
||||
|
||||
/**
|
||||
* Hooks all extensions metadata mapping drivers
|
||||
* into given $driverChain of drivers for ORM
|
||||
*
|
||||
* @param MappingDriverChain $driverChain
|
||||
* @param Reader|null $reader
|
||||
*/
|
||||
public static function registerMappingIntoDriverChainORM(MappingDriverChain $driverChain, Reader $reader = null)
|
||||
{
|
||||
self::registerAnnotations();
|
||||
if (!$reader) {
|
||||
$reader = new CachedReader(new AnnotationReader(), new ArrayCache());
|
||||
}
|
||||
$annotationDriver = new DriverORM\AnnotationDriver($reader, array(
|
||||
__DIR__.'/Translatable/Entity',
|
||||
__DIR__.'/Loggable/Entity',
|
||||
__DIR__.'/Tree/Entity',
|
||||
));
|
||||
$driverChain->addDriver($annotationDriver, 'Gedmo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks only superclass metadata mapping drivers
|
||||
* into given $driverChain of drivers for ORM
|
||||
*
|
||||
* @param MappingDriverChain $driverChain
|
||||
* @param Reader|null $reader
|
||||
*/
|
||||
public static function registerAbstractMappingIntoDriverChainORM(MappingDriverChain $driverChain, Reader $reader = null)
|
||||
{
|
||||
self::registerAnnotations();
|
||||
if (!$reader) {
|
||||
$reader = new CachedReader(new AnnotationReader(), new ArrayCache());
|
||||
}
|
||||
$annotationDriver = new DriverORM\AnnotationDriver($reader, array(
|
||||
__DIR__.'/Translatable/Entity/MappedSuperclass',
|
||||
__DIR__.'/Loggable/Entity/MappedSuperclass',
|
||||
__DIR__.'/Tree/Entity/MappedSuperclass',
|
||||
));
|
||||
$driverChain->addDriver($annotationDriver, 'Gedmo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks all extensions metadata mapping drivers
|
||||
* into given $driverChain of drivers for ODM MongoDB
|
||||
*
|
||||
* @param MappingDriverChain $driverChain
|
||||
* @param Reader|null $reader
|
||||
*/
|
||||
public static function registerMappingIntoDriverChainMongodbODM(MappingDriverChain $driverChain, Reader $reader = null)
|
||||
{
|
||||
self::registerAnnotations();
|
||||
if (!$reader) {
|
||||
$reader = new CachedReader(new AnnotationReader(), new ArrayCache());
|
||||
}
|
||||
$annotationDriver = new DriverMongodbODM\AnnotationDriver($reader, array(
|
||||
__DIR__.'/Translatable/Document',
|
||||
__DIR__.'/Loggable/Document',
|
||||
));
|
||||
$driverChain->addDriver($annotationDriver, 'Gedmo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks only superclass metadata mapping drivers
|
||||
* into given $driverChain of drivers for ODM MongoDB
|
||||
*
|
||||
* @param MappingDriverChain $driverChain
|
||||
* @param Reader|null $reader
|
||||
*/
|
||||
public static function registerAbstractMappingIntoDriverChainMongodbODM(MappingDriverChain $driverChain, Reader $reader = null)
|
||||
{
|
||||
self::registerAnnotations();
|
||||
if (!$reader) {
|
||||
$reader = new CachedReader(new AnnotationReader(), new ArrayCache());
|
||||
}
|
||||
$annotationDriver = new DriverMongodbODM\AnnotationDriver($reader, array(
|
||||
__DIR__.'/Translatable/Document/MappedSuperclass',
|
||||
__DIR__.'/Loggable/Document/MappedSuperclass',
|
||||
));
|
||||
$driverChain->addDriver($annotationDriver, 'Gedmo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes all extension annotations once
|
||||
*/
|
||||
public static function registerAnnotations()
|
||||
{
|
||||
AnnotationRegistry::registerFile(__DIR__.'/Mapping/Annotation/All.php');
|
||||
}
|
||||
}
|
||||
19
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception.php
vendored
Normal file
19
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception.php
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo;
|
||||
|
||||
/**
|
||||
* Common package exception interface to allow
|
||||
* users of caching only this package specific
|
||||
* exceptions thrown
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface Exception
|
||||
{
|
||||
/**
|
||||
* Following best practices for PHP5.3 package exceptions.
|
||||
* All exceptions thrown in this package will have to implement this interface
|
||||
*/
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/BadMethodCallException.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/BadMethodCallException.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* BadMethodCallException
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class BadMethodCallException
|
||||
extends \BadMethodCallException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/FeatureNotImplementedException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/FeatureNotImplementedException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* FeatureNotImplementedException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class FeatureNotImplementedException
|
||||
extends \RuntimeException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidArgumentException.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidArgumentException.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* InvalidArgumentException
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class InvalidArgumentException
|
||||
extends \InvalidArgumentException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
20
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidMappingException.php
vendored
Normal file
20
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/InvalidMappingException.php
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* InvalidMappingException
|
||||
*
|
||||
* Triggered when mapping user argument is not
|
||||
* valid or incomplete.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class InvalidMappingException
|
||||
extends InvalidArgumentException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
15
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/ReferenceIntegrityStrictException.php
vendored
Normal file
15
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/ReferenceIntegrityStrictException.php
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* ReferenceIntegrityStrictException
|
||||
*
|
||||
* @author Evert Harmeling <evert.harmeling@freshheads.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class ReferenceIntegrityStrictException extends RuntimeException
|
||||
{
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/RuntimeException.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/RuntimeException.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* RuntimeException
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class RuntimeException
|
||||
extends \RuntimeException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
16
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/TreeLockingException.php
vendored
Normal file
16
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/TreeLockingException.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* TreeLockingException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class TreeLockingException extends RuntimeException
|
||||
{
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnexpectedValueException.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnexpectedValueException.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UnexpectedValueException
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UnexpectedValueException
|
||||
extends \UnexpectedValueException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnsupportedObjectManagerException.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UnsupportedObjectManagerException.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UnsupportedObjectManager
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UnsupportedObjectManagerException
|
||||
extends InvalidArgumentException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCantWriteException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCantWriteException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableCantWriteException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableCantWriteException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCouldntGuessMimeTypeException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableCouldntGuessMimeTypeException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableCouldntGuessMimeTypeException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableCouldntGuessMimeTypeException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableDirectoryNotFoundException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableDirectoryNotFoundException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableDirectoryNotFoundException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableDirectoryNotFoundException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableException
|
||||
extends RuntimeException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableExtensionException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableExtensionException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableExtensionException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableExtensionException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileAlreadyExistsException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileAlreadyExistsException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableFileAlreadyExistsException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableFileAlreadyExistsException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileNotReadableException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFileNotReadableException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableFileNotReadableException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableFileNotReadableException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFormSizeException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableFormSizeException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableFormSizeException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableFormSizeException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableIniSizeException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableIniSizeException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableIniSizeException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableIniSizeException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidFileException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidFileException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableInvalidFileException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableInvalidFileException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidMimeTypeException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidMimeTypeException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableInvalidMimeTypeException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableInvalidMimeTypeException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidPathException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableInvalidPathException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableInvalidPathException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableInvalidPathException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableMaxSizeException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableMaxSizeException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableMaxSizeException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableMaxSizeException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoFileException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoFileException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableNoFileException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableNoFileException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoPathDefinedException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoPathDefinedException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableNoPathDefinedException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableNoPathDefinedException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoTmpDirException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableNoTmpDirException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableNoTmpDirException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableNoTmpDirException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadablePartialException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadablePartialException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadablePartialException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadablePartialException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableUploadException.php
vendored
Normal file
18
vendor/gedmo/doctrine-extensions/lib/Gedmo/Exception/UploadableUploadException.php
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Exception;
|
||||
|
||||
use Gedmo\Exception;
|
||||
|
||||
/**
|
||||
* UploadableUploadException
|
||||
*
|
||||
* @author Gustavo Falco <comfortablynumb84@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class UploadableUploadException
|
||||
extends UploadableException
|
||||
implements Exception
|
||||
{
|
||||
}
|
||||
50
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceable.php
vendored
Normal file
50
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceable.php
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable;
|
||||
|
||||
/**
|
||||
* This interface is not necessary but can be implemented for
|
||||
* Entities which in some cases needs to be identified as
|
||||
* IpTraceable
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface IpTraceable
|
||||
{
|
||||
// ipTraceable expects annotations on properties
|
||||
|
||||
/**
|
||||
* @gedmo:IpTraceable(on="create")
|
||||
* strings which should be updated on insert only
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:IpTraceable(on="update")
|
||||
* strings which should be updated on update and insert
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:IpTraceable(on="change", field="field", value="value")
|
||||
* strings which should be updated on changed "property"
|
||||
* value and become equal to given "value"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:IpTraceable(on="change", field="field")
|
||||
* strings which should be updated on changed "property"
|
||||
*/
|
||||
|
||||
/**
|
||||
* @gedmo:IpTraceable(on="change", fields={"field1", "field2"})
|
||||
* strings which should be updated if at least one of the given fields changed
|
||||
*/
|
||||
|
||||
/**
|
||||
* example
|
||||
*
|
||||
* @gedmo:IpTraceable(on="create")
|
||||
* @Column(type="string")
|
||||
* $created
|
||||
*/
|
||||
}
|
||||
59
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceableListener.php
vendored
Normal file
59
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/IpTraceableListener.php
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable;
|
||||
|
||||
use Gedmo\AbstractTrackingListener;
|
||||
use Gedmo\Exception\InvalidArgumentException;
|
||||
use Gedmo\Mapping\Event\AdapterInterface;
|
||||
|
||||
/**
|
||||
* The IpTraceable listener handles the update of
|
||||
* IPs on creation and update.
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class IpTraceableListener extends AbstractTrackingListener
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $ip;
|
||||
|
||||
/**
|
||||
* Get the ipValue value to set on a ip field
|
||||
*
|
||||
* @param object $meta
|
||||
* @param string $field
|
||||
* @param AdapterInterface $eventAdapter
|
||||
*
|
||||
* @return null|string
|
||||
*/
|
||||
public function getFieldValue($meta, $field, $eventAdapter)
|
||||
{
|
||||
return $this->ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a ip value to return
|
||||
*
|
||||
* @param string $ip
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function setIpValue($ip = null)
|
||||
{
|
||||
if (isset($ip) && filter_var($ip, FILTER_VALIDATE_IP) === false) {
|
||||
throw new InvalidArgumentException("ip address is not valid $ip");
|
||||
}
|
||||
|
||||
$this->ip = $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getNamespace()
|
||||
{
|
||||
return __NAMESPACE__;
|
||||
}
|
||||
}
|
||||
77
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Annotation.php
vendored
Normal file
77
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Annotation.php
vendored
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\AbstractAnnotationDriver;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is an annotation mapping driver for IpTraceable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from Annotations specifically for IpTraceable
|
||||
* extension.
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Annotation extends AbstractAnnotationDriver
|
||||
{
|
||||
/**
|
||||
* Annotation field is ipTraceable
|
||||
*/
|
||||
const IP_TRACEABLE = 'Gedmo\\Mapping\\Annotation\\IpTraceable';
|
||||
|
||||
/**
|
||||
* List of types which are valid for IP
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $validTypes = array(
|
||||
'string',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
$class = $this->getMetaReflectionClass($meta);
|
||||
// property annotations
|
||||
foreach ($class->getProperties() as $property) {
|
||||
if ($meta->isMappedSuperclass && !$property->isPrivate() ||
|
||||
$meta->isInheritedField($property->name) ||
|
||||
isset($meta->associationMappings[$property->name]['inherited'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if ($ipTraceable = $this->reader->getPropertyAnnotation($property, self::IP_TRACEABLE)) {
|
||||
$field = $property->getName();
|
||||
|
||||
if (!$meta->hasField($field)) {
|
||||
throw new InvalidMappingException("Unable to find ipTraceable [{$field}] as mapped property in entity - {$meta->name}");
|
||||
}
|
||||
if ($meta->hasField($field) && !$this->isValidField($meta, $field)) {
|
||||
throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' - {$meta->name}");
|
||||
}
|
||||
if (!in_array($ipTraceable->on, array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
if ($ipTraceable->on == 'change') {
|
||||
if (!isset($ipTraceable->field)) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
if (is_array($ipTraceable->field) && isset($ipTraceable->value)) {
|
||||
throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $ipTraceable->field,
|
||||
'value' => $ipTraceable->value,
|
||||
);
|
||||
}
|
||||
// properties are unique and mapper checks that, no risk here
|
||||
$config[$ipTraceable->on][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Xml.php
vendored
Normal file
133
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Xml.php
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\Xml as BaseXml;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is a xml mapping driver for IpTraceable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from xml specifically for IpTraceable
|
||||
* extension.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @author Miha Vrhovnik <miha.vrhovnik@gmail.com>
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Xml extends BaseXml
|
||||
{
|
||||
/**
|
||||
* List of types which are valid for IP
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $validTypes = array(
|
||||
'string',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
/**
|
||||
* @var \SimpleXmlElement $mapping
|
||||
*/
|
||||
$mapping = $this->_getMapping($meta->name);
|
||||
|
||||
if (isset($mapping->field)) {
|
||||
/**
|
||||
* @var \SimpleXmlElement $fieldMapping
|
||||
*/
|
||||
foreach ($mapping->field as $fieldMapping) {
|
||||
$fieldMappingDoctrine = $fieldMapping;
|
||||
$fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI);
|
||||
if (isset($fieldMapping->{'ip-traceable'})) {
|
||||
/**
|
||||
* @var \SimpleXmlElement $data
|
||||
*/
|
||||
$data = $fieldMapping->{'ip-traceable'};
|
||||
|
||||
$field = $this->_getAttribute($fieldMappingDoctrine, 'name');
|
||||
if (!$this->isValidField($meta, $field)) {
|
||||
throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' in class - {$meta->name}");
|
||||
}
|
||||
if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
|
||||
if ($this->_getAttribute($data, 'on') == 'change') {
|
||||
if (!$this->_isAttributeSet($data, 'field')) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
$trackedFieldAttribute = $this->_getAttribute($data, 'field');
|
||||
$valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null;
|
||||
if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
|
||||
throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $trackedFieldAttribute,
|
||||
'value' => $valueAttribute,
|
||||
);
|
||||
}
|
||||
$config[$this->_getAttribute($data, 'on')][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping->{'many-to-one'})) {
|
||||
foreach ($mapping->{'many-to-one'} as $fieldMapping) {
|
||||
$field = $this->_getAttribute($fieldMapping, 'field');
|
||||
$fieldMapping = $fieldMapping->children(self::GEDMO_NAMESPACE_URI);
|
||||
if (isset($fieldMapping->{'ip-traceable'})) {
|
||||
/**
|
||||
* @var \SimpleXmlElement $data
|
||||
*/
|
||||
$data = $fieldMapping->{'ip-traceable'};
|
||||
|
||||
if (! $meta->isSingleValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}");
|
||||
}
|
||||
if (!$this->_isAttributeSet($data, 'on') || !in_array($this->_getAttribute($data, 'on'), array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
|
||||
if ($this->_getAttribute($data, 'on') == 'change') {
|
||||
if (!$this->_isAttributeSet($data, 'field')) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
$trackedFieldAttribute = $this->_getAttribute($data, 'field');
|
||||
$valueAttribute = $this->_isAttributeSet($data, 'value') ? $this->_getAttribute($data, 'value' ) : null;
|
||||
if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
|
||||
throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $trackedFieldAttribute,
|
||||
'value' => $valueAttribute,
|
||||
);
|
||||
}
|
||||
$config[$this->_getAttribute($data, 'on')][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if $field type is valid
|
||||
*
|
||||
* @param object $meta
|
||||
* @param string $field
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isValidField($meta, $field)
|
||||
{
|
||||
$mapping = $meta->getFieldMapping($field);
|
||||
|
||||
return $mapping && in_array($mapping['type'], $this->validTypes);
|
||||
}
|
||||
}
|
||||
127
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Yaml.php
vendored
Normal file
127
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Driver/Yaml.php
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\File;
|
||||
use Gedmo\Mapping\Driver;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is a yaml mapping driver for IpTraceable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from yaml specifically for IpTraceable
|
||||
* extension.
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Yaml extends File implements Driver
|
||||
{
|
||||
/**
|
||||
* File extension
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.dcm.yml';
|
||||
|
||||
/**
|
||||
* List of types which are valid for IP
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $validTypes = array(
|
||||
'string',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
$mapping = $this->_getMapping($meta->name);
|
||||
|
||||
if (isset($mapping['fields'])) {
|
||||
foreach ($mapping['fields'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo']['ipTraceable'])) {
|
||||
$mappingProperty = $fieldMapping['gedmo']['ipTraceable'];
|
||||
if (!$this->isValidField($meta, $field)) {
|
||||
throw new InvalidMappingException("Field - [{$field}] type is not valid and must be 'string' in class - {$meta->name}");
|
||||
}
|
||||
if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
|
||||
if ($mappingProperty['on'] == 'change') {
|
||||
if (!isset($mappingProperty['field'])) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
$trackedFieldAttribute = $mappingProperty['field'];
|
||||
$valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null;
|
||||
if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
|
||||
throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $trackedFieldAttribute,
|
||||
'value' => $valueAttribute,
|
||||
);
|
||||
}
|
||||
$config[$mappingProperty['on']][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['manyToOne'])) {
|
||||
foreach ($mapping['manyToOne'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo']['ipTraceable'])) {
|
||||
$mappingProperty = $fieldMapping['gedmo']['ipTraceable'];
|
||||
if (! $meta->isSingleValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Association - [{$field}] is not valid, it must be a one-to-many relation or a string field - {$meta->name}");
|
||||
}
|
||||
if (!isset($mappingProperty['on']) || !in_array($mappingProperty['on'], array('update', 'create', 'change'))) {
|
||||
throw new InvalidMappingException("Field - [{$field}] trigger 'on' is not one of [update, create, change] in class - {$meta->name}");
|
||||
}
|
||||
|
||||
if ($mappingProperty['on'] == 'change') {
|
||||
if (!isset($mappingProperty['field'])) {
|
||||
throw new InvalidMappingException("Missing parameters on property - {$field}, field must be set on [change] trigger in class - {$meta->name}");
|
||||
}
|
||||
$trackedFieldAttribute = $mappingProperty['field'];
|
||||
$valueAttribute = isset($mappingProperty['value']) ? $mappingProperty['value'] : null;
|
||||
if (is_array($trackedFieldAttribute) && null !== $valueAttribute) {
|
||||
throw new InvalidMappingException("IpTraceable extension does not support multiple value changeset detection yet.");
|
||||
}
|
||||
$field = array(
|
||||
'field' => $field,
|
||||
'trackedField' => $trackedFieldAttribute,
|
||||
'value' => $valueAttribute,
|
||||
);
|
||||
}
|
||||
$config[$mappingProperty['on']][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _loadMappingFile($file)
|
||||
{
|
||||
return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if $field type is valid
|
||||
*
|
||||
* @param object $meta
|
||||
* @param string $field
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isValidField($meta, $field)
|
||||
{
|
||||
$mapping = $meta->getFieldMapping($field);
|
||||
|
||||
return $mapping && in_array($mapping['type'], $this->validTypes);
|
||||
}
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ODM.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ODM.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ODM as BaseAdapterODM;
|
||||
use Gedmo\IpTraceable\Mapping\Event\IpTraceableAdapter;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter for ODM adapted
|
||||
* for IpTraceable behavior
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class ODM extends BaseAdapterODM implements IpTraceableAdapter
|
||||
{
|
||||
}
|
||||
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ORM.php
vendored
Normal file
17
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/Adapter/ORM.php
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
|
||||
use Gedmo\IpTraceable\Mapping\Event\IpTraceableAdapter;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter for ORM adapted
|
||||
* for IpTraceable behavior
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class ORM extends BaseAdapterORM implements IpTraceableAdapter
|
||||
{
|
||||
}
|
||||
16
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/IpTraceableAdapter.php
vendored
Normal file
16
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Mapping/Event/IpTraceableAdapter.php
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Mapping\Event;
|
||||
|
||||
use Gedmo\Mapping\Event\AdapterInterface;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter interface
|
||||
* for IpTraceable behavior
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface IpTraceableAdapter extends AdapterInterface
|
||||
{
|
||||
}
|
||||
68
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceable.php
vendored
Normal file
68
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceable.php
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Traits;
|
||||
|
||||
/**
|
||||
* IpTraceable Trait, usable with PHP >= 5.4
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
trait IpTraceable
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $createdFromIp;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $updatedFromIp;
|
||||
|
||||
/**
|
||||
* Sets createdFromIp.
|
||||
*
|
||||
* @param string $createdFromIp
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreatedFromIp($createdFromIp)
|
||||
{
|
||||
$this->createdFromIp = $createdFromIp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns createdFromIp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedFromIp()
|
||||
{
|
||||
return $this->createdFromIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets updatedFromIp.
|
||||
*
|
||||
* @param string $updatedFromIp
|
||||
* @return $this
|
||||
*/
|
||||
public function setUpdatedFromIp($updatedFromIp)
|
||||
{
|
||||
$this->updatedFromIp = $updatedFromIp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns updatedFromIp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdatedFromIp()
|
||||
{
|
||||
return $this->updatedFromIp;
|
||||
}
|
||||
}
|
||||
75
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableDocument.php
vendored
Normal file
75
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableDocument.php
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Traits;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* IpTraceable Trait, usable with PHP >= 5.4
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
trait IpTraceableDocument
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Gedmo\IpTraceable(on="create")
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
protected $createdFromIp;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @Gedmo\IpTraceable(on="update")
|
||||
* @ODM\Field(type="string")
|
||||
*/
|
||||
protected $updatedFromIp;
|
||||
|
||||
/**
|
||||
* Sets createdFromIp.
|
||||
*
|
||||
* @param string $createdFromIp
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreatedFromIp($createdFromIp)
|
||||
{
|
||||
$this->createdFromIp = $createdFromIp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns createdFromIp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedFromIp()
|
||||
{
|
||||
return $this->createdFromIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets updatedFromIp.
|
||||
*
|
||||
* @param string $updatedFromIp
|
||||
* @return $this
|
||||
*/
|
||||
public function setUpdatedFromIp($updatedFromIp)
|
||||
{
|
||||
$this->updatedFromIp = $updatedFromIp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns updatedFromIp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdatedFromIp()
|
||||
{
|
||||
return $this->updatedFromIp;
|
||||
}
|
||||
}
|
||||
75
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableEntity.php
vendored
Normal file
75
vendor/gedmo/doctrine-extensions/lib/Gedmo/IpTraceable/Traits/IpTraceableEntity.php
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\IpTraceable\Traits;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Gedmo\Mapping\Annotation as Gedmo;
|
||||
|
||||
/**
|
||||
* IpTraceable Trait, usable with PHP >= 5.4
|
||||
*
|
||||
* @author Pierre-Charles Bertineau <pc.bertineau@alterphp.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
trait IpTraceableEntity
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @Gedmo\IpTraceable(on="create")
|
||||
* @ORM\Column(length=45, nullable=true)
|
||||
*/
|
||||
protected $createdFromIp;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @Gedmo\IpTraceable(on="update")
|
||||
* @ORM\Column(length=45, nullable=true)
|
||||
*/
|
||||
protected $updatedFromIp;
|
||||
|
||||
/**
|
||||
* Sets createdFromIp.
|
||||
*
|
||||
* @param string $createdFromIp
|
||||
* @return $this
|
||||
*/
|
||||
public function setCreatedFromIp($createdFromIp)
|
||||
{
|
||||
$this->createdFromIp = $createdFromIp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns createdFromIp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedFromIp()
|
||||
{
|
||||
return $this->createdFromIp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets updatedFromIp.
|
||||
*
|
||||
* @param string $updatedFromIp
|
||||
* @return $this
|
||||
*/
|
||||
public function setUpdatedFromIp($updatedFromIp)
|
||||
{
|
||||
$this->updatedFromIp = $updatedFromIp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns updatedFromIp.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUpdatedFromIp()
|
||||
{
|
||||
return $this->updatedFromIp;
|
||||
}
|
||||
}
|
||||
25
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/LogEntry.php
vendored
Normal file
25
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/LogEntry.php
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Document;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoODM;
|
||||
|
||||
/**
|
||||
* Gedmo\Loggable\Document\LogEntry
|
||||
*
|
||||
* @MongoODM\Document(
|
||||
* repositoryClass="Gedmo\Loggable\Document\Repository\LogEntryRepository",
|
||||
* indexes={
|
||||
* @MongoODM\Index(keys={"objectId"="asc", "objectClass"="asc", "version"="asc"}),
|
||||
* @MongoODM\Index(keys={"loggedAt"="asc"}),
|
||||
* @MongoODM\Index(keys={"objectClass"="asc"}),
|
||||
* @MongoODM\Index(keys={"username"="asc"})
|
||||
* }
|
||||
* )
|
||||
*/
|
||||
class LogEntry extends MappedSuperclass\AbstractLogEntry
|
||||
{
|
||||
/**
|
||||
* All required columns are mapped through inherited superclass
|
||||
*/
|
||||
}
|
||||
217
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/MappedSuperclass/AbstractLogEntry.php
vendored
Normal file
217
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/MappedSuperclass/AbstractLogEntry.php
vendored
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Document\MappedSuperclass;
|
||||
|
||||
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoODM;
|
||||
|
||||
/**
|
||||
* Gedmo\Loggable\Document\MappedSuperclass\AbstractLogEntry
|
||||
*
|
||||
* @MongoODM\MappedSuperclass
|
||||
*/
|
||||
abstract class AbstractLogEntry
|
||||
{
|
||||
/**
|
||||
* @var integer $id
|
||||
*
|
||||
* @MongoODM\Id
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string $action
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $action;
|
||||
|
||||
/**
|
||||
* @var \DateTime $loggedAt
|
||||
*
|
||||
* @MongoODM\Field(type="date")
|
||||
*/
|
||||
protected $loggedAt;
|
||||
|
||||
/**
|
||||
* @var string $objectId
|
||||
*
|
||||
* @MongoODM\Field(type="string", nullable=true)
|
||||
*/
|
||||
protected $objectId;
|
||||
|
||||
/**
|
||||
* @var string $objectClass
|
||||
*
|
||||
* @MongoODM\Field(type="string")
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* @var integer $version
|
||||
*
|
||||
* @MongoODM\Field(type="int")
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* @var string $data
|
||||
*
|
||||
* @MongoODM\Field(type="hash", nullable=true)
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var string $data
|
||||
*
|
||||
* @MongoODM\Field(type="string", nullable=true)
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAction()
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set action
|
||||
*
|
||||
* @param string $action
|
||||
*/
|
||||
public function setAction($action)
|
||||
{
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getObjectClass()
|
||||
{
|
||||
return $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object class
|
||||
*
|
||||
* @param string $objectClass
|
||||
*/
|
||||
public function setObjectClass($objectClass)
|
||||
{
|
||||
$this->objectClass = $objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getObjectId()
|
||||
{
|
||||
return $this->objectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object id
|
||||
*
|
||||
* @param string $objectId
|
||||
*/
|
||||
public function setObjectId($objectId)
|
||||
{
|
||||
$this->objectId = $objectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set username
|
||||
*
|
||||
* @param string $username
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loggedAt
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getLoggedAt()
|
||||
{
|
||||
return $this->loggedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set loggedAt to "now"
|
||||
*/
|
||||
public function setLoggedAt()
|
||||
{
|
||||
$this->loggedAt = new \DateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data
|
||||
*
|
||||
* @return array or null
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current version
|
||||
*
|
||||
* @param integer $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current version
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
}
|
||||
161
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/Repository/LogEntryRepository.php
vendored
Normal file
161
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Document/Repository/LogEntryRepository.php
vendored
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Document\Repository;
|
||||
|
||||
use Gedmo\Loggable\Document\LogEntry;
|
||||
use Gedmo\Tool\Wrapper\MongoDocumentWrapper;
|
||||
use Gedmo\Loggable\LoggableListener;
|
||||
use Doctrine\ODM\MongoDB\DocumentRepository;
|
||||
use Doctrine\ODM\MongoDB\Cursor;
|
||||
|
||||
/**
|
||||
* The LogEntryRepository has some useful functions
|
||||
* to interact with log entries.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class LogEntryRepository extends DocumentRepository
|
||||
{
|
||||
/**
|
||||
* Currently used loggable listener
|
||||
*
|
||||
* @var LoggableListener
|
||||
*/
|
||||
private $listener;
|
||||
|
||||
/**
|
||||
* Loads all log entries for the
|
||||
* given $document
|
||||
*
|
||||
* @param object $document
|
||||
*
|
||||
* @return LogEntry[]
|
||||
*/
|
||||
public function getLogEntries($document)
|
||||
{
|
||||
$wrapped = new MongoDocumentWrapper($document, $this->dm);
|
||||
$objectId = $wrapped->getIdentifier();
|
||||
|
||||
$qb = $this->createQueryBuilder();
|
||||
$qb->field('objectId')->equals($objectId);
|
||||
$qb->field('objectClass')->equals($wrapped->getMetadata()->name);
|
||||
$qb->sort('version', 'DESC');
|
||||
$q = $qb->getQuery();
|
||||
|
||||
$result = $q->execute();
|
||||
if ($result instanceof Cursor) {
|
||||
$result = $result->toArray();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts given $document to $revision by
|
||||
* restoring all fields from that $revision.
|
||||
* After this operation you will need to
|
||||
* persist and flush the $document.
|
||||
*
|
||||
* @param object $document
|
||||
* @param integer $version
|
||||
*
|
||||
* @throws \Gedmo\Exception\UnexpectedValueException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function revert($document, $version = 1)
|
||||
{
|
||||
$wrapped = new MongoDocumentWrapper($document, $this->dm);
|
||||
$objectMeta = $wrapped->getMetadata();
|
||||
$objectId = $wrapped->getIdentifier();
|
||||
|
||||
$qb = $this->createQueryBuilder();
|
||||
$qb->field('objectId')->equals($objectId);
|
||||
$qb->field('objectClass')->equals($objectMeta->name);
|
||||
$qb->field('version')->lte(intval($version));
|
||||
$qb->sort('version', 'ASC');
|
||||
$q = $qb->getQuery();
|
||||
|
||||
$logs = $q->execute();
|
||||
if ($logs instanceof Cursor) {
|
||||
$logs = $logs->toArray();
|
||||
}
|
||||
if ($logs) {
|
||||
$data = array();
|
||||
while (($log = array_shift($logs))) {
|
||||
$data = array_merge($data, $log->getData());
|
||||
}
|
||||
$this->fillDocument($document, $data, $objectMeta);
|
||||
} else {
|
||||
throw new \Gedmo\Exception\UnexpectedValueException('Count not find any log entries under version: '.$version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills a documents versioned fields with data
|
||||
*
|
||||
* @param object $document
|
||||
* @param array $data
|
||||
*/
|
||||
protected function fillDocument($document, array $data)
|
||||
{
|
||||
$wrapped = new MongoDocumentWrapper($document, $this->dm);
|
||||
$objectMeta = $wrapped->getMetadata();
|
||||
$config = $this->getLoggableListener()->getConfiguration($this->dm, $objectMeta->name);
|
||||
$fields = $config['versioned'];
|
||||
foreach ($data as $field => $value) {
|
||||
if (!in_array($field, $fields)) {
|
||||
continue;
|
||||
}
|
||||
$mapping = $objectMeta->getFieldMapping($field);
|
||||
// Fill the embedded document
|
||||
if ($wrapped->isEmbeddedAssociation($field)) {
|
||||
if (!empty($value)) {
|
||||
$embeddedMetadata = $this->dm->getClassMetadata($mapping['targetDocument']);
|
||||
$document = $embeddedMetadata->newInstance();
|
||||
$this->fillDocument($document, $value);
|
||||
$value = $document;
|
||||
}
|
||||
} elseif ($objectMeta->isSingleValuedAssociation($field)) {
|
||||
$value = $value ? $this->dm->getReference($mapping['targetDocument'], $value) : null;
|
||||
}
|
||||
$wrapped->setPropertyValue($field, $value);
|
||||
unset($fields[$field]);
|
||||
}
|
||||
|
||||
/*
|
||||
if (count($fields)) {
|
||||
throw new \Gedmo\Exception\UnexpectedValueException('Cound not fully revert the document to version: '.$version);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently used LoggableListener
|
||||
*
|
||||
* @throws \Gedmo\Exception\RuntimeException - if listener is not found
|
||||
*
|
||||
* @return LoggableListener
|
||||
*/
|
||||
private function getLoggableListener()
|
||||
{
|
||||
if (is_null($this->listener)) {
|
||||
foreach ($this->dm->getEventManager()->getListeners() as $event => $listeners) {
|
||||
foreach ($listeners as $hash => $listener) {
|
||||
if ($listener instanceof LoggableListener) {
|
||||
$this->listener = $listener;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($this->listener) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($this->listener)) {
|
||||
throw new \Gedmo\Exception\RuntimeException('The loggable listener could not be found');
|
||||
}
|
||||
}
|
||||
return $this->listener;
|
||||
}
|
||||
}
|
||||
27
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/LogEntry.php
vendored
Normal file
27
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/LogEntry.php
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Entity;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Gedmo\Loggable\Entity\LogEntry
|
||||
*
|
||||
* @ORM\Table(
|
||||
* name="ext_log_entries",
|
||||
* options={"row_format":"DYNAMIC"},
|
||||
* indexes={
|
||||
* @ORM\Index(name="log_class_lookup_idx", columns={"object_class"}),
|
||||
* @ORM\Index(name="log_date_lookup_idx", columns={"logged_at"}),
|
||||
* @ORM\Index(name="log_user_lookup_idx", columns={"username"}),
|
||||
* @ORM\Index(name="log_version_lookup_idx", columns={"object_id", "object_class", "version"})
|
||||
* }
|
||||
* )
|
||||
* @ORM\Entity(repositoryClass="Gedmo\Loggable\Entity\Repository\LogEntryRepository")
|
||||
*/
|
||||
class LogEntry extends MappedSuperclass\AbstractLogEntry
|
||||
{
|
||||
/**
|
||||
* All required columns are mapped through inherited superclass
|
||||
*/
|
||||
}
|
||||
219
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/MappedSuperclass/AbstractLogEntry.php
vendored
Normal file
219
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/MappedSuperclass/AbstractLogEntry.php
vendored
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Entity\MappedSuperclass;
|
||||
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Gedmo\Loggable\Entity\AbstractLog
|
||||
*
|
||||
* @ORM\MappedSuperclass
|
||||
*/
|
||||
abstract class AbstractLogEntry
|
||||
{
|
||||
/**
|
||||
* @var integer $id
|
||||
*
|
||||
* @ORM\Column(type="integer")
|
||||
* @ORM\Id
|
||||
* @ORM\GeneratedValue
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* @var string $action
|
||||
*
|
||||
* @ORM\Column(type="string", length=8)
|
||||
*/
|
||||
protected $action;
|
||||
|
||||
/**
|
||||
* @var \DateTime $loggedAt
|
||||
*
|
||||
* @ORM\Column(name="logged_at", type="datetime")
|
||||
*/
|
||||
protected $loggedAt;
|
||||
|
||||
/**
|
||||
* @var string $objectId
|
||||
*
|
||||
* @ORM\Column(name="object_id", length=64, nullable=true)
|
||||
*/
|
||||
protected $objectId;
|
||||
|
||||
/**
|
||||
* @var string $objectClass
|
||||
*
|
||||
* @ORM\Column(name="object_class", type="string", length=255)
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* @var integer $version
|
||||
*
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* @var array $data
|
||||
*
|
||||
* @ORM\Column(type="array", nullable=true)
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* @var string $data
|
||||
*
|
||||
* @ORM\Column(length=255, nullable=true)
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* Get id
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAction()
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set action
|
||||
*
|
||||
* @param string $action
|
||||
*/
|
||||
public function setAction($action)
|
||||
{
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getObjectClass()
|
||||
{
|
||||
return $this->objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object class
|
||||
*
|
||||
* @param string $objectClass
|
||||
*/
|
||||
public function setObjectClass($objectClass)
|
||||
{
|
||||
$this->objectClass = $objectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getObjectId()
|
||||
{
|
||||
return $this->objectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object id
|
||||
*
|
||||
* @param string $objectId
|
||||
*/
|
||||
public function setObjectId($objectId)
|
||||
{
|
||||
$this->objectId = $objectId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get username
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUsername()
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set username
|
||||
*
|
||||
* @param string $username
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loggedAt
|
||||
*
|
||||
* @return \DateTime
|
||||
*/
|
||||
public function getLoggedAt()
|
||||
{
|
||||
return $this->loggedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set loggedAt to "now"
|
||||
*/
|
||||
public function setLoggedAt()
|
||||
{
|
||||
$this->loggedAt = new \DateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set data
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function setData($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set current version
|
||||
*
|
||||
* @param integer $version
|
||||
*/
|
||||
public function setVersion($version)
|
||||
{
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current version
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getVersion()
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
}
|
||||
164
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/Repository/LogEntryRepository.php
vendored
Normal file
164
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Entity/Repository/LogEntryRepository.php
vendored
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Entity\Repository;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query;
|
||||
use Gedmo\Loggable\Entity\LogEntry;
|
||||
use Gedmo\Tool\Wrapper\EntityWrapper;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Gedmo\Loggable\LoggableListener;
|
||||
|
||||
/**
|
||||
* The LogEntryRepository has some useful functions
|
||||
* to interact with log entries.
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class LogEntryRepository extends EntityRepository
|
||||
{
|
||||
/**
|
||||
* Currently used loggable listener
|
||||
*
|
||||
* @var LoggableListener
|
||||
*/
|
||||
private $listener;
|
||||
|
||||
/**
|
||||
* Loads all log entries for the given entity
|
||||
*
|
||||
* @param object $entity
|
||||
*
|
||||
* @return LogEntry[]
|
||||
*/
|
||||
public function getLogEntries($entity)
|
||||
{
|
||||
$q = $this->getLogEntriesQuery($entity);
|
||||
|
||||
return $q->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the query for loading of log entries
|
||||
*
|
||||
* @param object $entity
|
||||
*
|
||||
* @return Query
|
||||
*/
|
||||
public function getLogEntriesQuery($entity)
|
||||
{
|
||||
$wrapped = new EntityWrapper($entity, $this->_em);
|
||||
$objectClass = $wrapped->getMetadata()->name;
|
||||
$meta = $this->getClassMetadata();
|
||||
$dql = "SELECT log FROM {$meta->name} log";
|
||||
$dql .= " WHERE log.objectId = :objectId";
|
||||
$dql .= " AND log.objectClass = :objectClass";
|
||||
$dql .= " ORDER BY log.version DESC";
|
||||
|
||||
$objectId = (string) $wrapped->getIdentifier();
|
||||
$q = $this->_em->createQuery($dql);
|
||||
$q->setParameters(compact('objectId', 'objectClass'));
|
||||
|
||||
return $q;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverts given $entity to $revision by
|
||||
* restoring all fields from that $revision.
|
||||
* After this operation you will need to
|
||||
* persist and flush the $entity.
|
||||
*
|
||||
* @param object $entity
|
||||
* @param integer $version
|
||||
*
|
||||
* @throws \Gedmo\Exception\UnexpectedValueException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function revert($entity, $version = 1)
|
||||
{
|
||||
$wrapped = new EntityWrapper($entity, $this->_em);
|
||||
$objectMeta = $wrapped->getMetadata();
|
||||
$objectClass = $objectMeta->name;
|
||||
$meta = $this->getClassMetadata();
|
||||
$dql = "SELECT log FROM {$meta->name} log";
|
||||
$dql .= " WHERE log.objectId = :objectId";
|
||||
$dql .= " AND log.objectClass = :objectClass";
|
||||
$dql .= " AND log.version <= :version";
|
||||
$dql .= " ORDER BY log.version ASC";
|
||||
|
||||
$objectId = (string) $wrapped->getIdentifier();
|
||||
$q = $this->_em->createQuery($dql);
|
||||
$q->setParameters(compact('objectId', 'objectClass', 'version'));
|
||||
$logs = $q->getResult();
|
||||
|
||||
if ($logs) {
|
||||
$config = $this->getLoggableListener()->getConfiguration($this->_em, $objectMeta->name);
|
||||
$fields = $config['versioned'];
|
||||
$filled = false;
|
||||
while (($log = array_pop($logs)) && !$filled) {
|
||||
if ($data = $log->getData()) {
|
||||
foreach ($data as $field => $value) {
|
||||
if (in_array($field, $fields)) {
|
||||
$this->mapValue($objectMeta, $field, $value);
|
||||
$wrapped->setPropertyValue($field, $value);
|
||||
unset($fields[array_search($field, $fields)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$filled = count($fields) === 0;
|
||||
}
|
||||
/*if (count($fields)) {
|
||||
throw new \Gedmo\Exception\UnexpectedValueException('Could not fully revert the entity to version: '.$version);
|
||||
}*/
|
||||
} else {
|
||||
throw new \Gedmo\Exception\UnexpectedValueException('Could not find any log entries under version: '.$version);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMetadata $objectMeta
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
*/
|
||||
protected function mapValue(ClassMetadata $objectMeta, $field, &$value)
|
||||
{
|
||||
if (!$objectMeta->isSingleValuedAssociation($field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mapping = $objectMeta->getAssociationMapping($field);
|
||||
$value = $value ? $this->_em->getReference($mapping['targetEntity'], $value) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently used LoggableListener
|
||||
*
|
||||
* @throws \Gedmo\Exception\RuntimeException - if listener is not found
|
||||
*
|
||||
* @return LoggableListener
|
||||
*/
|
||||
private function getLoggableListener()
|
||||
{
|
||||
if (is_null($this->listener)) {
|
||||
foreach ($this->_em->getEventManager()->getListeners() as $event => $listeners) {
|
||||
foreach ($listeners as $hash => $listener) {
|
||||
if ($listener instanceof LoggableListener) {
|
||||
$this->listener = $listener;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($this->listener) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_null($this->listener)) {
|
||||
throw new \Gedmo\Exception\RuntimeException('The loggable listener could not be found');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->listener;
|
||||
}
|
||||
}
|
||||
28
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Loggable.php
vendored
Normal file
28
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Loggable.php
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable;
|
||||
|
||||
/**
|
||||
* This interface is not necessary but can be implemented for
|
||||
* Domain Objects which in some cases needs to be identified as
|
||||
* Loggable
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface Loggable
|
||||
{
|
||||
// this interface is not necessary to implement
|
||||
|
||||
/**
|
||||
* @gedmo:Loggable
|
||||
* to mark the class as loggable use class annotation @gedmo:Loggable
|
||||
* this object will contain now a history
|
||||
* available options:
|
||||
* logEntryClass="My\LogEntryObject" (optional) defaultly will use internal object class
|
||||
* example:
|
||||
*
|
||||
* @gedmo:Loggable(logEntryClass="My\LogEntryObject")
|
||||
* class MyEntity
|
||||
*/
|
||||
}
|
||||
324
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/LoggableListener.php
vendored
Normal file
324
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/LoggableListener.php
vendored
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Gedmo\Mapping\MappedEventSubscriber;
|
||||
use Gedmo\Loggable\Mapping\Event\LoggableAdapter;
|
||||
use Gedmo\Tool\Wrapper\AbstractWrapper;
|
||||
|
||||
/**
|
||||
* Loggable listener
|
||||
*
|
||||
* @author Boussekeyt Jules <jules.boussekeyt@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class LoggableListener extends MappedEventSubscriber
|
||||
{
|
||||
/**
|
||||
* Create action
|
||||
*/
|
||||
const ACTION_CREATE = 'create';
|
||||
|
||||
/**
|
||||
* Update action
|
||||
*/
|
||||
const ACTION_UPDATE = 'update';
|
||||
|
||||
/**
|
||||
* Remove action
|
||||
*/
|
||||
const ACTION_REMOVE = 'remove';
|
||||
|
||||
/**
|
||||
* Username for identification
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $username;
|
||||
|
||||
/**
|
||||
* List of log entries which do not have the foreign
|
||||
* key generated yet - MySQL case. These entries
|
||||
* will be updated with new keys on postPersist event
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pendingLogEntryInserts = array();
|
||||
|
||||
/**
|
||||
* For log of changed relations we use
|
||||
* its identifiers to avoid storing serialized Proxies.
|
||||
* These are pending relations in case it does not
|
||||
* have an identifier yet
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $pendingRelatedObjects = array();
|
||||
|
||||
/**
|
||||
* Set username for identification
|
||||
*
|
||||
* @param mixed $username
|
||||
*
|
||||
* @throws \Gedmo\Exception\InvalidArgumentException Invalid username
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
if (is_string($username)) {
|
||||
$this->username = $username;
|
||||
} elseif (is_object($username) && method_exists($username, 'getUsername')) {
|
||||
$this->username = (string) $username->getUsername();
|
||||
} else {
|
||||
throw new \Gedmo\Exception\InvalidArgumentException("Username must be a string, or object should have method: getUsername");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
'onFlush',
|
||||
'loadClassMetadata',
|
||||
'postPersist',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LogEntry class
|
||||
*
|
||||
* @param LoggableAdapter $ea
|
||||
* @param string $class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getLogEntryClass(LoggableAdapter $ea, $class)
|
||||
{
|
||||
return isset(self::$configurations[$this->name][$class]['logEntryClass']) ?
|
||||
self::$configurations[$this->name][$class]['logEntryClass'] :
|
||||
$ea->getDefaultLogEntryClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps additional metadata
|
||||
*
|
||||
* @param EventArgs $eventArgs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadClassMetadata(EventArgs $eventArgs)
|
||||
{
|
||||
$ea = $this->getEventAdapter($eventArgs);
|
||||
$this->loadMetadataForObjectClass($ea->getObjectManager(), $eventArgs->getClassMetadata());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for inserted object to update its logEntry
|
||||
* foreign key
|
||||
*
|
||||
* @param EventArgs $args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function postPersist(EventArgs $args)
|
||||
{
|
||||
$ea = $this->getEventAdapter($args);
|
||||
$object = $ea->getObject();
|
||||
$om = $ea->getObjectManager();
|
||||
$oid = spl_object_hash($object);
|
||||
$uow = $om->getUnitOfWork();
|
||||
if ($this->pendingLogEntryInserts && array_key_exists($oid, $this->pendingLogEntryInserts)) {
|
||||
$wrapped = AbstractWrapper::wrap($object, $om);
|
||||
|
||||
$logEntry = $this->pendingLogEntryInserts[$oid];
|
||||
$logEntryMeta = $om->getClassMetadata(get_class($logEntry));
|
||||
|
||||
$id = $wrapped->getIdentifier();
|
||||
$logEntryMeta->getReflectionProperty('objectId')->setValue($logEntry, $id);
|
||||
$uow->scheduleExtraUpdate($logEntry, array(
|
||||
'objectId' => array(null, $id),
|
||||
));
|
||||
$ea->setOriginalObjectProperty($uow, spl_object_hash($logEntry), 'objectId', $id);
|
||||
unset($this->pendingLogEntryInserts[$oid]);
|
||||
}
|
||||
if ($this->pendingRelatedObjects && array_key_exists($oid, $this->pendingRelatedObjects)) {
|
||||
$wrapped = AbstractWrapper::wrap($object, $om);
|
||||
$identifiers = $wrapped->getIdentifier(false);
|
||||
foreach ($this->pendingRelatedObjects[$oid] as $props) {
|
||||
$logEntry = $props['log'];
|
||||
$logEntryMeta = $om->getClassMetadata(get_class($logEntry));
|
||||
$oldData = $data = $logEntry->getData();
|
||||
$data[$props['field']] = $identifiers;
|
||||
|
||||
$logEntry->setData($data);
|
||||
|
||||
$uow->scheduleExtraUpdate($logEntry, array(
|
||||
'data' => array($oldData, $data),
|
||||
));
|
||||
$ea->setOriginalObjectProperty($uow, spl_object_hash($logEntry), 'data', $data);
|
||||
}
|
||||
unset($this->pendingRelatedObjects[$oid]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle any custom LogEntry functionality that needs to be performed
|
||||
* before persisting it
|
||||
*
|
||||
* @param object $logEntry The LogEntry being persisted
|
||||
* @param object $object The object being Logged
|
||||
*/
|
||||
protected function prePersistLogEntry($logEntry, $object)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for loggable objects being inserted or updated
|
||||
* for further processing
|
||||
*
|
||||
* @param EventArgs $eventArgs
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function onFlush(EventArgs $eventArgs)
|
||||
{
|
||||
$ea = $this->getEventAdapter($eventArgs);
|
||||
$om = $ea->getObjectManager();
|
||||
$uow = $om->getUnitOfWork();
|
||||
|
||||
foreach ($ea->getScheduledObjectInsertions($uow) as $object) {
|
||||
$this->createLogEntry(self::ACTION_CREATE, $object, $ea);
|
||||
}
|
||||
foreach ($ea->getScheduledObjectUpdates($uow) as $object) {
|
||||
$this->createLogEntry(self::ACTION_UPDATE, $object, $ea);
|
||||
}
|
||||
foreach ($ea->getScheduledObjectDeletions($uow) as $object) {
|
||||
$this->createLogEntry(self::ACTION_REMOVE, $object, $ea);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function getNamespace()
|
||||
{
|
||||
return __NAMESPACE__;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an objects changeset data
|
||||
*
|
||||
* @param LoggableAdapter $ea
|
||||
* @param object $object
|
||||
* @param object $logEntry
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getObjectChangeSetData($ea, $object, $logEntry)
|
||||
{
|
||||
$om = $ea->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $om);
|
||||
$meta = $wrapped->getMetadata();
|
||||
$config = $this->getConfiguration($om, $meta->name);
|
||||
$uow = $om->getUnitOfWork();
|
||||
$newValues = array();
|
||||
|
||||
foreach ($ea->getObjectChangeSet($uow, $object) as $field => $changes) {
|
||||
if (empty($config['versioned']) || !in_array($field, $config['versioned'])) {
|
||||
continue;
|
||||
}
|
||||
$value = $changes[1];
|
||||
if ($meta->isSingleValuedAssociation($field) && $value) {
|
||||
if ($wrapped->isEmbeddedAssociation($field)) {
|
||||
$value = $this->getObjectChangeSetData($ea, $value, $logEntry);
|
||||
} else {
|
||||
$oid = spl_object_hash($value);
|
||||
$wrappedAssoc = AbstractWrapper::wrap($value, $om);
|
||||
$value = $wrappedAssoc->getIdentifier(false);
|
||||
if (!is_array($value) && !$value) {
|
||||
$this->pendingRelatedObjects[$oid][] = array(
|
||||
'log' => $logEntry,
|
||||
'field' => $field,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
$newValues[$field] = $value;
|
||||
}
|
||||
|
||||
return $newValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Log instance
|
||||
*
|
||||
* @param string $action
|
||||
* @param object $object
|
||||
* @param LoggableAdapter $ea
|
||||
*
|
||||
* @return \Gedmo\Loggable\Entity\MappedSuperclass\AbstractLogEntry|null
|
||||
*/
|
||||
protected function createLogEntry($action, $object, LoggableAdapter $ea)
|
||||
{
|
||||
$om = $ea->getObjectManager();
|
||||
$wrapped = AbstractWrapper::wrap($object, $om);
|
||||
$meta = $wrapped->getMetadata();
|
||||
|
||||
// Filter embedded documents
|
||||
if (isset($meta->isEmbeddedDocument) && $meta->isEmbeddedDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($config = $this->getConfiguration($om, $meta->name)) {
|
||||
$logEntryClass = $this->getLogEntryClass($ea, $meta->name);
|
||||
$logEntryMeta = $om->getClassMetadata($logEntryClass);
|
||||
/** @var \Gedmo\Loggable\Entity\LogEntry $logEntry */
|
||||
$logEntry = $logEntryMeta->newInstance();
|
||||
|
||||
$logEntry->setAction($action);
|
||||
$logEntry->setUsername($this->username);
|
||||
$logEntry->setObjectClass($meta->name);
|
||||
$logEntry->setLoggedAt();
|
||||
|
||||
// check for the availability of the primary key
|
||||
$uow = $om->getUnitOfWork();
|
||||
if ($action === self::ACTION_CREATE && $ea->isPostInsertGenerator($meta)) {
|
||||
$this->pendingLogEntryInserts[spl_object_hash($object)] = $logEntry;
|
||||
} else {
|
||||
$logEntry->setObjectId($wrapped->getIdentifier());
|
||||
}
|
||||
$newValues = array();
|
||||
if ($action !== self::ACTION_REMOVE && isset($config['versioned'])) {
|
||||
$newValues = $this->getObjectChangeSetData($ea, $object, $logEntry);
|
||||
$logEntry->setData($newValues);
|
||||
}
|
||||
|
||||
if($action === self::ACTION_UPDATE && 0 === count($newValues)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$version = 1;
|
||||
if ($action !== self::ACTION_CREATE) {
|
||||
$version = $ea->getNewVersion($logEntryMeta, $object);
|
||||
if (empty($version)) {
|
||||
// was versioned later
|
||||
$version = 1;
|
||||
}
|
||||
}
|
||||
$logEntry->setVersion($version);
|
||||
|
||||
$this->prePersistLogEntry($logEntry, $object);
|
||||
|
||||
$om->persist($logEntry);
|
||||
$uow->computeChangeSet($logEntryMeta, $logEntry);
|
||||
|
||||
return $logEntry;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
140
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Annotation.php
vendored
Normal file
140
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Annotation.php
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Mapping\Driver;
|
||||
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
use Gedmo\Mapping\Driver\AbstractAnnotationDriver;
|
||||
|
||||
/**
|
||||
* This is an annotation mapping driver for Loggable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from Annotations specifically for Loggable
|
||||
* extension.
|
||||
*
|
||||
* @author Boussekeyt Jules <jules.boussekeyt@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Annotation extends AbstractAnnotationDriver
|
||||
{
|
||||
/**
|
||||
* Annotation to define that this object is loggable
|
||||
*/
|
||||
const LOGGABLE = 'Gedmo\\Mapping\\Annotation\\Loggable';
|
||||
|
||||
/**
|
||||
* Annotation to define that this property is versioned
|
||||
*/
|
||||
const VERSIONED = 'Gedmo\\Mapping\\Annotation\\Versioned';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function validateFullMetadata(ClassMetadata $meta, array $config)
|
||||
{
|
||||
if ($config && is_array($meta->identifier) && count($meta->identifier) > 1) {
|
||||
throw new InvalidMappingException("Loggable does not support composite identifiers in class - {$meta->name}");
|
||||
}
|
||||
if (isset($config['versioned']) && !isset($config['loggable'])) {
|
||||
throw new InvalidMappingException("Class must be annotated with Loggable annotation in order to track versioned fields in class - {$meta->name}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
$class = $this->getMetaReflectionClass($meta);
|
||||
// class annotations
|
||||
if ($annot = $this->reader->getClassAnnotation($class, self::LOGGABLE)) {
|
||||
$config['loggable'] = true;
|
||||
if ($annot->logEntryClass) {
|
||||
if (!$cl = $this->getRelatedClassName($meta, $annot->logEntryClass)) {
|
||||
throw new InvalidMappingException("LogEntry class: {$annot->logEntryClass} does not exist.");
|
||||
}
|
||||
$config['logEntryClass'] = $cl;
|
||||
}
|
||||
}
|
||||
|
||||
// property annotations
|
||||
foreach ($class->getProperties() as $property) {
|
||||
$field = $property->getName();
|
||||
if ($meta->isMappedSuperclass && !$property->isPrivate()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// versioned property
|
||||
if ($this->reader->getPropertyAnnotation($property, self::VERSIONED)) {
|
||||
if (!$this->isMappingValid($meta, $field)) {
|
||||
throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}");
|
||||
}
|
||||
if (isset($meta->embeddedClasses[$field])) {
|
||||
$this->inspectEmbeddedForVersioned($field, $config, $meta);
|
||||
continue;
|
||||
}
|
||||
// fields cannot be overrided and throws mapping exception
|
||||
if (!(isset($config['versioned']) && in_array($field, $config['versioned']))) {
|
||||
$config['versioned'][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$meta->isMappedSuperclass && $config) {
|
||||
if (is_array($meta->identifier) && count($meta->identifier) > 1) {
|
||||
throw new InvalidMappingException("Loggable does not support composite identifiers in class - {$meta->name}");
|
||||
}
|
||||
if ($this->isClassAnnotationInValid($meta, $config)) {
|
||||
throw new InvalidMappingException("Class must be annotated with Loggable annotation in order to track versioned fields in class - {$meta->name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMetadata $meta
|
||||
* @param string $field
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isMappingValid(ClassMetadata $meta, $field)
|
||||
{
|
||||
return $meta->isCollectionValuedAssociation($field) == false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMetadata $meta
|
||||
* @param array $config
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isClassAnnotationInValid(ClassMetadata $meta, array &$config)
|
||||
{
|
||||
return isset($config['versioned']) && !isset($config['loggable']) && (!isset($meta->isEmbeddedClass) || !$meta->isEmbeddedClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches properties of embedded object for versioned fields
|
||||
*
|
||||
* @param string $field
|
||||
* @param array $config
|
||||
* @param \Doctrine\ORM\Mapping\ClassMetadata $meta
|
||||
*/
|
||||
private function inspectEmbeddedForVersioned($field, array &$config, \Doctrine\ORM\Mapping\ClassMetadata $meta)
|
||||
{
|
||||
$сlass = new \ReflectionClass($meta->embeddedClasses[$field]['class']);
|
||||
|
||||
// property annotations
|
||||
foreach ($сlass->getProperties() as $property) {
|
||||
// versioned property
|
||||
if ($this->reader->getPropertyAnnotation($property, self::VERSIONED)) {
|
||||
$embeddedField = $field . '.' . $property->getName();
|
||||
$config['versioned'][] = $embeddedField;
|
||||
|
||||
if (isset($meta->embeddedClasses[$embeddedField])) {
|
||||
$this->inspectEmbeddedForVersioned($embeddedField, $config, $meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Xml.php
vendored
Normal file
104
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Xml.php
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\Xml as BaseXml;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is a xml mapping driver for Loggable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from xml specifically for Loggable
|
||||
* extension.
|
||||
*
|
||||
* @author Boussekeyt Jules <jules.boussekeyt@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @author Miha Vrhovnik <miha.vrhovnik@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Xml extends BaseXml
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
/**
|
||||
* @var \SimpleXmlElement $xml
|
||||
*/
|
||||
$xml = $this->_getMapping($meta->name);
|
||||
$xmlDoctrine = $xml;
|
||||
|
||||
$xml = $xml->children(self::GEDMO_NAMESPACE_URI);
|
||||
|
||||
if ($xmlDoctrine->getName() == 'entity' || $xmlDoctrine->getName() == 'document' || $xmlDoctrine->getName() == 'mapped-superclass') {
|
||||
if (isset($xml->loggable)) {
|
||||
/**
|
||||
* @var \SimpleXMLElement $data;
|
||||
*/
|
||||
$data = $xml->loggable;
|
||||
$config['loggable'] = true;
|
||||
if ($this->_isAttributeSet($data, 'log-entry-class')) {
|
||||
$class = $this->_getAttribute($data, 'log-entry-class');
|
||||
if (!$cl = $this->getRelatedClassName($meta, $class)) {
|
||||
throw new InvalidMappingException("LogEntry class: {$class} does not exist.");
|
||||
}
|
||||
$config['logEntryClass'] = $cl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($xmlDoctrine->field)) {
|
||||
$this->inspectElementForVersioned($xmlDoctrine->field, $config, $meta);
|
||||
}
|
||||
if (isset($xmlDoctrine->{'many-to-one'})) {
|
||||
$this->inspectElementForVersioned($xmlDoctrine->{'many-to-one'}, $config, $meta);
|
||||
}
|
||||
if (isset($xmlDoctrine->{'one-to-one'})) {
|
||||
$this->inspectElementForVersioned($xmlDoctrine->{'one-to-one'}, $config, $meta);
|
||||
}
|
||||
if (isset($xmlDoctrine->{'reference-one'})) {
|
||||
$this->inspectElementForVersioned($xmlDoctrine->{'reference-one'}, $config, $meta);
|
||||
}
|
||||
if (isset($xmlDoctrine->{'embedded'})) {
|
||||
$this->inspectElementForVersioned($xmlDoctrine->{'embedded'}, $config, $meta);
|
||||
}
|
||||
|
||||
if (!$meta->isMappedSuperclass && $config) {
|
||||
if (is_array($meta->identifier) && count($meta->identifier) > 1) {
|
||||
throw new InvalidMappingException("Loggable does not support composite identifiers in class - {$meta->name}");
|
||||
}
|
||||
if (isset($config['versioned']) && !isset($config['loggable'])) {
|
||||
throw new InvalidMappingException("Class must be annotated with Loggable annotation in order to track versioned fields in class - {$meta->name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches mappings on element for versioned fields
|
||||
*
|
||||
* @param \SimpleXMLElement $element
|
||||
* @param array $config
|
||||
* @param object $meta
|
||||
*/
|
||||
private function inspectElementForVersioned(\SimpleXMLElement $element, array &$config, $meta)
|
||||
{
|
||||
foreach ($element as $mapping) {
|
||||
$mappingDoctrine = $mapping;
|
||||
/**
|
||||
* @var \SimpleXmlElement $mapping
|
||||
*/
|
||||
$mapping = $mapping->children(self::GEDMO_NAMESPACE_URI);
|
||||
|
||||
$isAssoc = $this->_isAttributeSet($mappingDoctrine, 'field');
|
||||
$field = $this->_getAttribute($mappingDoctrine, $isAssoc ? 'field' : 'name');
|
||||
|
||||
if (isset($mapping->versioned)) {
|
||||
if ($isAssoc && !$meta->associationMappings[$field]['isOwningSide']) {
|
||||
throw new InvalidMappingException("Cannot version [{$field}] as it is not the owning side in object - {$meta->name}");
|
||||
}
|
||||
$config['versioned'][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Yaml.php
vendored
Normal file
149
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Driver/Yaml.php
vendored
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Mapping\Driver;
|
||||
|
||||
use Gedmo\Mapping\Driver\File;
|
||||
use Gedmo\Mapping\Driver;
|
||||
use Gedmo\Exception\InvalidMappingException;
|
||||
|
||||
/**
|
||||
* This is a yaml mapping driver for Loggable
|
||||
* behavioral extension. Used for extraction of extended
|
||||
* metadata from yaml specifically for Loggable
|
||||
* extension.
|
||||
*
|
||||
* @author Boussekeyt Jules <jules.boussekeyt@gmail.com>
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
class Yaml extends File implements Driver
|
||||
{
|
||||
/**
|
||||
* File extension
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.dcm.yml';
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function readExtendedMetadata($meta, array &$config)
|
||||
{
|
||||
$mapping = $this->_getMapping($meta->name);
|
||||
|
||||
if (isset($mapping['gedmo'])) {
|
||||
$classMapping = $mapping['gedmo'];
|
||||
if (isset($classMapping['loggable'])) {
|
||||
$config['loggable'] = true;
|
||||
if (isset ($classMapping['loggable']['logEntryClass'])) {
|
||||
if (!$cl = $this->getRelatedClassName($meta, $classMapping['loggable']['logEntryClass'])) {
|
||||
throw new InvalidMappingException("LogEntry class: {$classMapping['loggable']['logEntryClass']} does not exist.");
|
||||
}
|
||||
$config['logEntryClass'] = $cl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['fields'])) {
|
||||
foreach ($mapping['fields'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo'])) {
|
||||
if (in_array('versioned', $fieldMapping['gedmo'])) {
|
||||
if ($meta->isCollectionValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}");
|
||||
}
|
||||
// fields cannot be overrided and throws mapping exception
|
||||
$config['versioned'][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['attributeOverride'])) {
|
||||
foreach ($mapping['attributeOverride'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo'])) {
|
||||
if (in_array('versioned', $fieldMapping['gedmo'])) {
|
||||
if ($meta->isCollectionValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}");
|
||||
}
|
||||
// fields cannot be overrided and throws mapping exception
|
||||
$config['versioned'][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['manyToOne'])) {
|
||||
foreach ($mapping['manyToOne'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo'])) {
|
||||
if (in_array('versioned', $fieldMapping['gedmo'])) {
|
||||
if ($meta->isCollectionValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}");
|
||||
}
|
||||
// fields cannot be overrided and throws mapping exception
|
||||
$config['versioned'][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['oneToOne'])) {
|
||||
foreach ($mapping['oneToOne'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo'])) {
|
||||
if (in_array('versioned', $fieldMapping['gedmo'])) {
|
||||
if ($meta->isCollectionValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}");
|
||||
}
|
||||
// fields cannot be overrided and throws mapping exception
|
||||
$config['versioned'][] = $field;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($mapping['embedded'])) {
|
||||
foreach ($mapping['embedded'] as $field => $fieldMapping) {
|
||||
if (isset($fieldMapping['gedmo'])) {
|
||||
if (in_array('versioned', $fieldMapping['gedmo'])) {
|
||||
if ($meta->isCollectionValuedAssociation($field)) {
|
||||
throw new InvalidMappingException("Cannot apply versioning to field [{$field}] as it is collection in object - {$meta->name}");
|
||||
}
|
||||
// fields cannot be overrided and throws mapping exception
|
||||
$mapping = $this->_getMapping($fieldMapping['class']);
|
||||
$this->inspectEmbeddedForVersioned($field, $mapping, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$meta->isMappedSuperclass && $config) {
|
||||
if (is_array($meta->identifier) && count($meta->identifier) > 1) {
|
||||
throw new InvalidMappingException("Loggable does not support composite identifiers in class - {$meta->name}");
|
||||
}
|
||||
if (isset($config['versioned']) && !isset($config['loggable'])) {
|
||||
throw new InvalidMappingException("Class must be annoted with Loggable annotation in order to track versioned fields in class - {$meta->name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function _loadMappingFile($file)
|
||||
{
|
||||
return \Symfony\Component\Yaml\Yaml::parse(file_get_contents($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $field
|
||||
* @param array $mapping
|
||||
* @param array $config
|
||||
*/
|
||||
private function inspectEmbeddedForVersioned($field, array $mapping, array &$config)
|
||||
{
|
||||
if (isset($mapping['fields'])) {
|
||||
foreach ($mapping['fields'] as $property => $fieldMapping) {
|
||||
$config['versioned'][] = $field . '.' . $property;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ODM.php
vendored
Normal file
59
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ODM.php
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ODM as BaseAdapterODM;
|
||||
use Gedmo\Loggable\Mapping\Event\LoggableAdapter;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter for ODM adapted
|
||||
* for Loggable behavior
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class ODM extends BaseAdapterODM implements LoggableAdapter
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getDefaultLogEntryClass()
|
||||
{
|
||||
return 'Gedmo\\Loggable\\Document\\LogEntry';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isPostInsertGenerator($meta)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getNewVersion($meta, $object)
|
||||
{
|
||||
$dm = $this->getObjectManager();
|
||||
$objectMeta = $dm->getClassMetadata(get_class($object));
|
||||
$identifierField = $this->getSingleIdentifierFieldName($objectMeta);
|
||||
$objectId = $objectMeta->getReflectionProperty($identifierField)->getValue($object);
|
||||
|
||||
$qb = $dm->createQueryBuilder($meta->name);
|
||||
$qb->select('version');
|
||||
$qb->field('objectId')->equals($objectId);
|
||||
$qb->field('objectClass')->equals($objectMeta->name);
|
||||
$qb->sort('version', 'DESC');
|
||||
$qb->limit(1);
|
||||
$q = $qb->getQuery();
|
||||
$q->setHydrate(false);
|
||||
|
||||
$result = $q->getSingleResult();
|
||||
if ($result) {
|
||||
$result = $result['version'] + 1;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
55
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ORM.php
vendored
Normal file
55
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/Adapter/ORM.php
vendored
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Mapping\Event\Adapter;
|
||||
|
||||
use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
|
||||
use Gedmo\Loggable\Mapping\Event\LoggableAdapter;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter for ORM adapted
|
||||
* for Loggable behavior
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
final class ORM extends BaseAdapterORM implements LoggableAdapter
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getDefaultLogEntryClass()
|
||||
{
|
||||
return 'Gedmo\\Loggable\\Entity\\LogEntry';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isPostInsertGenerator($meta)
|
||||
{
|
||||
return $meta->idGenerator->isPostInsertGenerator();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getNewVersion($meta, $object)
|
||||
{
|
||||
$em = $this->getObjectManager();
|
||||
$objectMeta = $em->getClassMetadata(get_class($object));
|
||||
$identifierField = $this->getSingleIdentifierFieldName($objectMeta);
|
||||
$objectId = (string) $objectMeta->getReflectionProperty($identifierField)->getValue($object);
|
||||
|
||||
$dql = "SELECT MAX(log.version) FROM {$meta->name} log";
|
||||
$dql .= " WHERE log.objectId = :objectId";
|
||||
$dql .= " AND log.objectClass = :objectClass";
|
||||
|
||||
$q = $em->createQuery($dql);
|
||||
$q->setParameters(array(
|
||||
'objectId' => $objectId,
|
||||
'objectClass' => $objectMeta->name,
|
||||
));
|
||||
|
||||
return $q->getSingleScalarResult() + 1;
|
||||
}
|
||||
}
|
||||
39
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/LoggableAdapter.php
vendored
Normal file
39
vendor/gedmo/doctrine-extensions/lib/Gedmo/Loggable/Mapping/Event/LoggableAdapter.php
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Gedmo\Loggable\Mapping\Event;
|
||||
|
||||
use Gedmo\Mapping\Event\AdapterInterface;
|
||||
|
||||
/**
|
||||
* Doctrine event adapter interface
|
||||
* for Loggable behavior
|
||||
*
|
||||
* @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
|
||||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
interface LoggableAdapter extends AdapterInterface
|
||||
{
|
||||
/**
|
||||
* Get default LogEntry class used to store the logs
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultLogEntryClass();
|
||||
|
||||
/**
|
||||
* Checks whether an id should be generated post insert
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function isPostInsertGenerator($meta);
|
||||
|
||||
/**
|
||||
* Get new version number
|
||||
*
|
||||
* @param object $meta
|
||||
* @param object $object
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function getNewVersion($meta, $object);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue