Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Field slugifier id generator #585

Open
wants to merge 2 commits into
base: 2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"doctrine/instantiator": "~1.0.1"
},
"require-dev": {
"aferrandini/urlizer": "1.0.*",
"symfony/yaml": "~2.0",
"liip/rmt": "~1.1"
},
Expand Down
23 changes: 23 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -382,4 +382,27 @@ public function getUuidGenerator()
}
;
}

/**
* Set slugifier callable
*
* PHP callable for peroforming slugification when using the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/peroforming/performing

* slugify from field ID generator method
*
* @param Callable
*/
public function setSlugifier($callable)
{
$this->slugifier = $callable;
}

/**
* Get slugifier callable
*
* @return callable
*/
public function getSlugifier()
{
return $this->slugifier;
}
}
129 changes: 129 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Id/FieldSlugifierIdGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/

namespace Doctrine\ODM\PHPCR\Id;

use Doctrine\ODM\PHPCR\DocumentManager;
use Doctrine\ODM\PHPCR\Mapping\ClassMetadata;
use PHPCR\RepositoryException;
use PHPCR\Util\NodeHelper;

/**
* Generate the id using the auto naming strategy
*/
class FieldSlugifierIdGenerator extends ParentIdGenerator
{
/**
* @var callable
*/
private $slugifier;

/**
* @param callable Slugifier callable
*/
public function __construct($slugifier)
{
$this->slugifier = $slugifier;
}

/**
* Use the parent field together with an auto generated name to generate the id
*
* {@inheritDoc}
*/
public function generate($document, ClassMetadata $class, DocumentManager $dm, $parent = null)
{
if (null === $parent) {
$parent = $class->parentMapping ? $class->getFieldValue($document, $class->parentMapping) : null;
}

if (null === $parent) {
throw IdException::noIdNoParent($document, $class->parentMapping);
}

if (!isset($class->idGeneratorOptions['field'])) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess we can't detect this misconfiguration earlier?

throw new \InvalidArgumentException(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doctrine exceptions are supposed to have static factory methods to have all exceptions in one central place.

'The field slugifier ID generator requires that you specify the '.
'"field" option specifying the field to be slugified.'
);
}

$fieldName = $class->idGeneratorOptions['field'];

if (!$class->hasField($fieldName)) {
throw new \InvalidArgumentException(sprintf(
'"%s" has been specified as the field to be slugified by the ' .
'field slugifier ID generator. But it is not mapped',
$fieldName
));
}

$value = $class->getFieldValue($document, $fieldName);

if (!$value) {
throw new \InvalidArgumentException(sprintf(
'Cannot slugify node name from empty field value for field "%s"',
$fieldName
));
}

$slugified = $this->slugify($value);

$parentId = $dm->getUnitOfWork()->getDocumentId($parent);

return $parentId . '/' . $slugified;
}

/**
* Try and call the slugifier
*/
private function slugify($string)
{
static $resolvedSlugifier = null;

if ($resolvedSlugifier) {
return $resolvedSlugifier($string);
}

$slugifier = $this->slugifier;

if ($slugifier instanceof \Closure) {
return $resolvedSlugifier = function ($string) use ($slugifier) {
$slugifier($string);
};
}

if (is_array($string)) {
return $resolvedSlugifier = function ($string) use ($slugifier) {
call_user_func_array($slugifier[0], $slugifier[1]);
};
}

if (is_string($string)) {
return $resolvedSlugifier = function ($string) use ($slugifier) {
call_user_func($string);
};
}

throw new \InvalidArgumentException(sprintf(
'Could not call given slugifier callable of type "%s"',
gettype($string)
));
}
}
7 changes: 6 additions & 1 deletion lib/Doctrine/ODM/PHPCR/Id/IdGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
use Doctrine\ODM\PHPCR\DocumentManager;
use Doctrine\ODM\PHPCR\Mapping\ClassMetadata;
use Doctrine\ODM\PHPCR\Exception\InvalidArgumentException;
use Doctrine\ODM\PHPCR\Configuration;
use Doctrine\ODM\PHPCR\Id\FieldSlugifierIdGenerator;

/**
* Used to abstract ID generation
Expand All @@ -41,7 +43,7 @@ abstract class IdGenerator
*
* @return IdGenerator
*/
public static function create($generatorType)
public static function create($generatorType, Configuration $config)
{
switch ($generatorType) {
case ClassMetadata::GENERATOR_TYPE_ASSIGNED:
Expand All @@ -56,6 +58,9 @@ public static function create($generatorType)
case ClassMetadata::GENERATOR_TYPE_AUTO:
$instance = new AutoIdGenerator();
break;
case ClassMetadata::GENERATOR_TYPE_FIELD_SLUGIFIER:
$instance = new FieldSlugifierIdGenerator($config->getSlugifier());
break;

default:
throw new InvalidArgumentException("ID Generator does not exist: $generatorType");
Expand Down
35 changes: 35 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Mapping/Annotations/AssocArray.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/

namespace Doctrine\ODM\PHPCR\Mapping\Annotations;

use Doctrine\Common\Annotations\Annotation;

/**
* @Annotation
* @Target("PROPERTY")
*/
final class AssocArray extends TranslatableProperty
{
/**
* @var string
*/
public $type = 'assoc-array';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems unrelated to the slugifier id strategy

}

5 changes: 5 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Mapping/Annotations/Id.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,9 @@ final class Id
* @var string
*/
public $strategy;

/**
* @var array
*/
public $options;
}
13 changes: 13 additions & 0 deletions lib/Doctrine/ODM/PHPCR/Mapping/ClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ class ClassMetadata implements ClassMetadataInterface
*/
const GENERATOR_TYPE_AUTO = 4;

/**
* Slugify the value of a specified field and use that as the node name
*/
const GENERATOR_TYPE_FIELD_SLUGIFIER = 5;

protected static $validVersionableAnnotations = array('simple', 'full');

/**
Expand All @@ -104,6 +109,13 @@ class ClassMetadata implements ClassMetadataInterface
*/
public $idGenerator = self::GENERATOR_TYPE_NONE;

/**
* READ-ONLY: The options for the ID generator
*
* @var array
*/
public $idGeneratorOptions = array();

/**
* READ-ONLY: The field name of the document identifier.
*/
Expand Down Expand Up @@ -659,6 +671,7 @@ public function mapId(array $mapping, ClassMetadata $inherited = null)
$this->setIdentifier($mapping['fieldName']);
if (isset($mapping['strategy'])) {
$this->setIdGenerator($mapping['strategy']);
$this->idGeneratorOptions = isset($mapping['options']) ? : array();
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Doctrine/ODM/PHPCR/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -892,7 +892,7 @@ private function cascadeScheduleParentInsert($class, $document, &$visited)
private function getIdGenerator($type)
{
if (!isset($this->idGenerators[$type])) {
$this->idGenerators[$type] = IdGenerator::create($type);
$this->idGenerators[$type] = IdGenerator::create($type, $config);
}

return $this->idGenerators[$type];
Expand Down
18 changes: 18 additions & 0 deletions tests/Doctrine/Tests/Models/CMS/CmsBlock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Doctrine\Tests\Models\CMS;

use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRODM;

/**
* @PHPCRODM\Document
*/
class CmsBlock
{
/** @PHPCRODM\Id */
public $id;

/** @PHPCRODM\PhpArray */
public $config;
}

61 changes: 61 additions & 0 deletions tests/Doctrine/Tests/ODM/PHPCR/Functional/PhpArrayTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace Doctrine\Tests\ODM\PHPCR\Functional;

use Doctrine\ODM\PHPCR\Id\RepositoryIdInterface;
use Doctrine\ODM\PHPCR\DocumentRepository;
use Doctrine\ODM\PHPCR\Exception\InvalidArgumentException;
use PHPCR\PropertyType;
use PHPCR\Util\UUIDHelper;

use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRODM;
use Doctrine\Tests\Models\CMS\CmsBlock;

/**
* @group functional
*/
class PhpArrayTest extends \Doctrine\Tests\ODM\PHPCR\PHPCRFunctionalTestCase
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bah .. not part of this PR

{
/**
* @var \Doctrine\ODM\PHPCR\DocumentManager
*/
private $dm;

public function setUp()
{
$this->dm = $this->createDocumentManager(array(__DIR__));
$this->node = $this->resetFunctionalNode($this->dm);
}

public function providePersist()
{
return array(
array(
array(
'rows' => 3,
'title' => 'Foobar',
),
)
);
}

/**
* @dataProvider providePersist
*/
public function testPersist($config)
{
$block = new CmsBlock();
$block->id = '/functional/test';
$block->config = $config;
$this->dm->persist($block);
$this->dm->flush();
$this->dm->clear();

$block = $this->dm->find(null, '/functional/test');
$this->assertSame(
$config,
$block->config
);
}
}

Loading