Skip to content

Commit

Permalink
Add the mapping option for the PropelParamConverter,
Browse files Browse the repository at this point in the history
enhance the pk handling to use the real PK name and not only 'id',
fix new sensioFrameworkBundle 2.1 requirement:
ParamConverterInterface::apply() method now must return a Boolean value indicating if a conversion was done
  • Loading branch information
jaugustin committed May 22, 2012
1 parent f675e39 commit b03f6a5
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 10 deletions.
70 changes: 60 additions & 10 deletions Request/ParamConverter/PropelParamConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,75 @@


/**
* PropelConverter.
* PropelParamConverter
*
* This convert action parameter to a Propel Object
* there is two option for this converter:
*
* mapping : take an array of routeParam => column
* exclude : take an array of routeParam to exclude from the conversion process
*
*
* @author Jérémie Augustin <jeremie.augustin@pixel-cookers.com>
*/
class PropelParamConverter implements ParamConverterInterface
{
/**
* the pk column (e.g. id)
* @var string
*/
protected $pk;

/**
* list of column/value to use with filterBy
* @var array
*/
protected $filters = array();

/**
* list of route parameters to exclude from the conversion process
* @var array
*/
protected $exclude = array();

public function apply(Request $request, ConfigurationInterface $configuration)
{
$classQuery = $configuration->getClass() . 'Query';
$classPeer = $configuration->getClass() . 'Peer';

if (!class_exists($classQuery)) {
throw new \Exception(sprintf('The %s Query class does not exist', $classQuery));
}

$tableMap = $classPeer::getTableMap();
$pkColumns = $tableMap->getPrimaryKeyColumns();

if (count($pkColumns) == 1) {
$this->pk = strtolower($pkColumns[0]->getName());
}

$options = $configuration->getOptions();
$exclude = isset($options['exclude'])? $options['exclude'] : array();

if (isset($options['mapping'])) {
// We use the mapping for calling findPk or filterBy
foreach ($options['mapping'] as $routeParam => $column) {
if ($request->attributes->has($routeParam)) {
if ($this->pk === $column) {
$this->pk = $routeParam;
} else {
$this->filters[$column] = $request->attributes->get($routeParam);

This comment has been minimized.

Copy link
@prgTW

prgTW Jun 3, 2013

Using filters without resetting the array causes invalid behavior when converting parameters in subrequests in symfony. When master request uses this converter for f.ex. a $city in SomeController::someAction(City $city) with mapping "city": "slug", and agin in a subrequest with a different mapping the filters array has mapping from the master request, which causes to generate invalid queies to find a matching row from the database.

FIX (please review):

$this->filters = array();

at the beginning of the apply function

This comment has been minimized.

Copy link
@jaugustin

jaugustin Jun 3, 2013

Author Member

@prgTW thanks

}
}
}
} else {
$this->exclude = isset($options['exclude'])? $options['exclude'] : array();
$this->filters = $request->attributes->all();
}

// find by Pk
if (in_array('id', $exclude) || false === $object = $this->findPk($classQuery, $request)) {
if (false === $object = $this->findPk($classQuery, $request)) {
// find by criteria
if (false === $object = $this->findOneBy($classQuery, $request, $exclude)) {
if (false === $object = $this->findOneBy($classQuery, $request)) {
if ($configuration->isOptional()) {
//we find nothing but the object is optional
$object = null;
Expand All @@ -45,25 +93,27 @@ public function apply(Request $request, ConfigurationInterface $configuration)
}

$request->attributes->set($configuration->getName(), $object);

return true;
}

protected function findPk($classQuery, Request $request)
{
if (!$request->attributes->has('id')) {
if (in_array($this->pk, $this->exclude) || !$request->attributes->has($this->pk)) {
return false;
}

return $classQuery::create()->findPk($request->attributes->get('id'));
return $classQuery::create()->findPk($request->attributes->get($this->pk));
}

protected function findOneBy($classQuery, Request $request, $exclude)
protected function findOneBy($classQuery, Request $request)
{
$query = $classQuery::create();
$hasCriteria = false;
foreach ($request->attributes->all() as $key => $value) {
if (!in_array($key, $exclude)) {
foreach ($this->filters as $column => $value) {
if (!in_array($column, $this->exclude)) {
try {
$query->{'filterBy' . PropelInflector::camelize($key)}($value);
$query->{'filterBy' . PropelInflector::camelize($column)}($value);
$hasCriteria = true;
} catch (\PropelException $e) { }
}
Expand Down
18 changes: 18 additions & 0 deletions Resources/doc/param_converter.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,23 @@ public function myAction(Post $post)
}
```

#### Custom mapping ####

You can map route parameters directly to model column to be use for filtering.

If you have a route like `/my-route/{postUniqueName}/{AuthorId}`
Mapping option overwrite any other automatic mapping.

``` php
<?php

/**
* @ParamConverter("post", class="BlogBundle\Model\Post", options={"mapping"={"postUniqueName":"name"}})
* @ParamConverter("author", class="BlogBundle\Model\Author", options={"mapping"={"AuthorId":"id"}})
*/
public function myAction(Post $post, $author)
{
}
```

[Back to index](index.markdown)
26 changes: 26 additions & 0 deletions Tests/Request/ParamConverter/PropelParamConverterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,30 @@ public function testParamConverterFindWithOptionalParam()
$this->assertNull($request->attributes->get('book'),
'param "book" should be null if book is not found and the parameter is optional');
}

public function testParamConverterFindWithMapping()
{
$paramConverter = new PropelParamConverter();
$request = new Request(array(), array(), array('toto' => 1, 'book' => null));
$configuration = new ParamConverter(array('class' => 'Propel\PropelBundle\Tests\Fixtures\Model\Book',
'name' => 'book',
'options' => array('mapping' => array('toto' => 'id'))
));
$paramConverter->apply($request, $configuration);
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\Model\Book',$request->attributes->get('book'),
'param "book" should be an instance of "Propel\PropelBundle\Tests\Fixtures\Model\Book"');
}

public function testParamConverterFindSlugWithMapping()
{
$paramConverter = new PropelParamConverter();
$request = new Request(array(), array(), array('slugParam_special' => 'my-book', 'book' => null));
$configuration = new ParamConverter(array('class' => 'Propel\PropelBundle\Tests\Fixtures\Model\Book',
'name' => 'book',
'options' => array('mapping' => array('slugParam_special' => 'slug'))
));
$paramConverter->apply($request, $configuration);
$this->assertInstanceOf('Propel\PropelBundle\Tests\Fixtures\Model\Book',$request->attributes->get('book'),
'param "book" should be an instance of "Propel\PropelBundle\Tests\Fixtures\Model\Book"');
}
}

0 comments on commit b03f6a5

Please sign in to comment.