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

Add lookup strategy to writer #11

Open
wants to merge 2 commits into
base: master
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
116 changes: 52 additions & 64 deletions src/DoctrineWriter.php
Expand Up @@ -3,6 +3,7 @@
namespace Port\Doctrine;

use Port\Doctrine\Exception\UnsupportedDatabaseTypeException;
use Port\Doctrine\LookupStrategy\FieldsLookupStrategy;
use Port\Writer;
use Doctrine\Common\Util\Inflector;
use Doctrine\DBAL\Logging\SQLLogger;
Expand All @@ -26,20 +27,6 @@ class DoctrineWriter implements Writer, Writer\FlushableWriter
*/
protected $objectManager;

/**
* Fully qualified model name
*
* @var string
*/
protected $objectName;

/**
* Doctrine object repository
*
* @var ObjectRepository
*/
protected $objectRepository;

/**
* @var ClassMetadata
*/
Expand All @@ -60,57 +47,68 @@ class DoctrineWriter implements Writer, Writer\FlushableWriter
protected $truncate = true;

/**
* List of fields used to lookup an object
*
* @var array
* @var LookupStrategy
*/
protected $lookupFields = [];
private $lookupStrategy;

/**
* Method used for looking up the item
* Create a Doctrine writer with an object lookup strategy.
*
* @param string $objectName
* @param ObjectManager $objectManager
* @param LookupStrategy $lookupStrategy
*
* @var array
* @return self
*/
protected $lookupMethod;
public static function withLookupStrategy(
$objectName,
ObjectManager $objectManager,
LookupStrategy $lookupStrategy
) {
return new self($objectManager, $objectName, null, $lookupStrategy);
}

/**
* Constructor
*
* @param ObjectManager $objectManager
* @param string $objectName
* @param string|array $index Field or fields to find current entities by
* @param string $lookupMethod Method used for looking up the item
* @param ObjectManager $objectManager
* @param string $objectName
* @param string|array $index Field or fields to find current
* entities by
* @param string $lookupMethod Method used for looking up the item
* @param LookupStrategy $lookupStrategy
*
* @throws UnsupportedDatabaseTypeException
*/
public function __construct(
ObjectManager $objectManager,
$objectName,
Copy link
Member Author

Choose a reason for hiding this comment

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

$objectName needs to passed both here and when constructing a LookupStrategy. Not very elegant, but I don’t see any other options now.

$index = null,
$lookupMethod = 'findOneBy'
$lookupMethod = 'findOneBy',
LookupStrategy $lookupStrategy = null
) {
if ($index !== null || $lookupMethod !== 'findOneBy') {
@trigger_error(
'The $index and $lookupMethod arguments are deprecated. '
. 'Please use DoctrineWriter::withLookupStrategy() instead',
E_USER_DEPRECATED
);
}

$this->ensureSupportedObjectManager($objectManager);
$this->objectManager = $objectManager;
$this->objectRepository = $objectManager->getRepository($objectName);
$this->objectMetadata = $objectManager->getClassMetadata($objectName);
//translate objectName in case a namespace alias is used
$this->objectName = $this->objectMetadata->getName();
if ($index) {
if (is_array($index)) {
$this->lookupFields = $index;
} else {
$this->lookupFields = [$index];

if ($objectManager !== null && $index !== null) {
$lookupStrategy = (new FieldsLookupStrategy($objectManager, $objectName))
->withIndex($index);

if ($lookupMethod) {
$lookupStrategy = $lookupStrategy->withLookupMethod($lookupMethod);
}
}

if (!method_exists($this->objectRepository, $lookupMethod)) {
throw new \InvalidArgumentException(
sprintf(
'Repository %s has no method %s',
get_class($this->objectRepository),
$lookupMethod
)
);
$this->lookupStrategy = $lookupStrategy;
}
$this->lookupMethod = [$this->objectRepository, $lookupMethod];
}

/**
Expand Down Expand Up @@ -148,9 +146,9 @@ public function disableTruncate()
}

/**
* Disable Doctrine logging
* {@inheritdoc}
*
* @return $this
* Disable Doctrine logging
*/
public function prepare()
{
Expand All @@ -162,6 +160,8 @@ public function prepare()
}

/**
* {@inheritdoc}
*
* Re-enable Doctrine logging
*/
public function finish()
Expand Down Expand Up @@ -189,7 +189,7 @@ public function writeItem(array $item)
public function flush()
{
$this->objectManager->flush();
$this->objectManager->clear($this->objectName);
$this->objectManager->clear($this->objectMetadata->getName());
}

/**
Expand Down Expand Up @@ -284,7 +284,9 @@ protected function truncateTable()
$query = $connection->getDatabasePlatform()->getTruncateTableSQL($tableName, true);
$connection->executeQuery($query);
} elseif ($this->objectManager instanceof \Doctrine\ODM\MongoDB\DocumentManager) {
$this->objectManager->getDocumentCollection($this->objectName)->remove(array());
$this->objectManager->getDocumentCollection(
$this->objectMetadata->getName()
)->remove([]);
}
}

Expand Down Expand Up @@ -320,27 +322,13 @@ protected function reEnableLogging()
*/
protected function findOrCreateItem(array $item)
{
$object = null;
// If the table was not truncated to begin with, find current object
// first
if (!$this->truncate) {
if (!empty($this->lookupFields)) {
$lookupConditions = array();
foreach ($this->lookupFields as $fieldName) {
$lookupConditions[$fieldName] = $item[$fieldName];
}

$object = call_user_func($this->lookupMethod, $lookupConditions);
} else {
$object = $this->objectRepository->find(current($item));
}
}

if (!$object) {
return $this->getNewInstance();
return $this->lookupStrategy->lookup($item);
}

return $object;
return $this->getNewInstance();
}

protected function ensureSupportedObjectManager(ObjectManager $objectManager)
Expand Down
18 changes: 18 additions & 0 deletions src/LookupStrategy.php
@@ -0,0 +1,18 @@
<?php

namespace Port\Doctrine;

/**
* Finds existing objects in the database.
*/
interface LookupStrategy
{
/**
* Look up an item in the database.
*
* @param array $item
*
* @return mixed | null Null if no object was found.
*/
public function lookup(array $item);
}
107 changes: 107 additions & 0 deletions src/LookupStrategy/FieldsLookupStrategy.php
@@ -0,0 +1,107 @@
<?php

namespace Port\Doctrine\LookupStrategy;

use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;
use Port\Doctrine\LookupStrategy;

/**
* Default lookup strategy using object fields.
*/
class FieldsLookupStrategy implements LookupStrategy
{
/**
* @var ObjectManager
*/
private $objectManager;

/**
* @var ObjectRepository
*/
private $objectRepository;

/**
* @var array
*/
private $lookupFields;

/**
* @var string
*/
private $lookupMethod = 'findOneBy';

/**
* @param ObjectManager $objectManager
* @param string $objectName Fully qualified model name
*/
public function __construct(ObjectManager $objectManager, $objectName)
{
$this->objectManager = $objectManager;
$this->objectRepository = $objectManager->getRepository($objectName);
$this->lookupFields = $objectManager->getClassMetadata($objectName)->getIdentifierFieldNames();
}

/**
* @param string $field Field to current current objects by.
*
* @return self
*/
public function withLookupField($field)
{
return $this->withLookupFields([$field]);
}

/**
* Create lookup strategy with index
*
* @param array $fields Fields to find current objects by.
*
* @return self
*/
public function withLookupFields(array $fields)
{
$new = clone $this;
$this->lookupFields = $fields;

return $new;
}

/**
* Doctrine repository method for finding objects.
*
* @param string $lookupMethod
*
* @return self
*/
public function withLookupMethod($lookupMethod)
{
if (!method_exists($this->objectRepository, $lookupMethod)) {
throw new \InvalidArgumentException(
sprintf(
'Repository %s has no method %s',
get_class($this->objectRepository),
$lookupMethod
)
);
}

$new = clone $this;
$new->lookupMethod = [$this->objectRepository, $lookupMethod];

return $new;
}

/**
* {@inheritdoc}
*/
public function lookup(array $item)
{
$lookupConditions = array();
foreach ($this->lookupFields as $fieldName) {
$lookupConditions[$fieldName] = $item[$fieldName];
}

return call_user_func($this->lookupMethod, $lookupConditions);
}
}