Multi Property Magic Finder

If you ever developed an extbase extension you might have used the TxExtbasePersistenceRepository (or a subclass of it) to get your domain objects. If you implement an own repository for your domain models, you will usually extend TxExtbasePersistenceRepository and implement some special finder methods.

Today i thought it would be cool to have an even more magic magic finder. Wouldn’t it be great to match multiple properties in one finder? What about getting all „red“ objects in size „XL“?

TxExtbasePersistence_Repository already provides some magic finder. Without any additional code, you can find all objects matching a property on a given value. E.g. if you have a model with a string property named color, you can easily find all objects by calling $repository->findByColor('red'). Even if you didn’t explicit implement a findByColor method. It’s implemented by a magic method called „__call“, which parses the called method name and builds a certain query. However. If you’re used to extbase you already know that.

Now back to our „problem“, how to match multiple proptery in on finder.

One (obvious) solution would be implementing an explicit findByColorAndSize method in your repository. Creating a QueryObject, add two property constraints linked by logicalAnd, execute, return. As usual.

Another solution is two override the „__call“ method by this:

/**
 * Dispatches magic methods (findBy[Property]())
 *
 * @param string $methodName The name of the magic method
 * @param string $arguments The arguments of the magic method
 * @throws Tx_Extbase_Persistence_Exception_UnsupportedMethod
 * @return void
 * @api
 */
public function __call($methodName, $arguments) {
	if (substr($methodName, 0, 6) === 'findBy'
	  && strlen($methodName) > 7) {
		$query = $this->createQueryFromConstraintString(
			substr($methodName, 6), $arguments
		);
		return $query->execute();
 
	} elseif (substr($methodName, 0, 9) === 'findOneBy'
	  && strlen($methodName) > 10) {
		$query = $this->createQueryFromConstraintString(
			substr($methodName, 9),
			$arguments
		);
		$object = $query->setLimit(1)->execute()->getFirst();
		return $object;
 
	} elseif (substr($methodName, 0, 7) === 'countBy'
	  && strlen($methodName) > 8) {
		$query = $this->createQueryFromConstraintString(
			substr($methodName, 7),
			$arguments
		);
		$result = $query->execute()->count();
		return $result;
	}
	throw new Tx_Extbase_Persistence_Exception_UnsupportedMethod(
		'The method "' . $methodName . '" is not supported by the repository.',
		1233180480);
}
/**
 * @param string $constrainString
 * @param array $arguments
 * @return Tx_Extbase_Persistence_QueryInterface
 */
protected function createQueryFromConstraintString($constraintString, $arguments) {
	$query = $this->createQuery();
	$constraints = array();
	$argumentPointer = 0;
	if (strstr($constraintString, 'And')) {
		foreach (explode('And', $constraintString) as $propertyName) {
			$propertyName = strtolower(substr($propertyName, 0, 1) ) .
				substr($propertyName, 1);
			$constraints[] = $query->equals(
				$propertyName,
				$arguments[$argumentPointer]
			);
			$argumentPointer++;
		}
		$query->matching($query->logicalAnd($constraints));
	} elseif (strstr($constraintString, 'Or')) {
		foreach (explode('Or', $constraintString) as $propertyName) {
			$propertyName = strtolower(substr($propertyName, 0, 1) ) .
				substr($propertyName, 1);
			$constraints[] = $query->equals(
				$propertyName,
				$arguments[$argumentPointer]
			);
			$argumentPointer++;
		}
		$query->matching($query->logicalOr($constraints));
	} else {
		$propertyName = strtolower(substr($constraintString, 0, 1) ) .
			substr($constraintString, 1);
		$query->matching($query->equals($propertyName, $arguments[0]));
	}
	return $query;
}

I’ve cleaned up the __call method a little. The called method name (e.g. findByColorAndSize) is chopped and the second part (ColorAndSize) passed to createQueryFromConstraintString. This parses the string for „And“ or „Or“, gets all property names an builds a certain query object.

Using this new magic __call method, it’s possible to use finder like:

$repository->findByColorAndSize('red', 'XL');
$repository->findByColorAndSizeAndPrice('red', 'XL', 10.99);
$repository->findByFooOrBar('foo', 'bar');
$repository->findOneByFooOrBar('foo', 'bar');
$repository->countByFooOrBar('foo', 'bar');
// ...

I think you get the point.

2 Gedanken zu „Multi Property Magic Finder

  1. Andreas

    Hi,

    thanks for sharing this nice script. It is really usefull. There is a typo in the line „substr($constrainString, 1);“ at the bottom. A „t“ is missing. Without that regular queries (without AND/OR) won’t work.

    Schönen Abend Andreas

Kommentare sind geschlossen.