Skip to content

Commit

Permalink
Admin can have multiple parents now (sonata-project#5058)
Browse files Browse the repository at this point in the history
Co-authored-by: Fran Moreno <[email protected]>
Co-authored-by: Moi <[email protected]>
  • Loading branch information
3 people authored and greg0ire committed Apr 12, 2018
1 parent d77cd98 commit d0d9b3d
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 40 deletions.
20 changes: 20 additions & 0 deletions UPGRADE-3.x.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
UPGRADE 3.x
===========

## Multiple parents

Admin classes can now have multiple parents, when registering the service
you should pass a field name:

```
<service id="sonata.admin.playlist" class="App\Admin\PlaylistAdmin">
<!-- ... -->
<call method="addChild">
<argument type="service" id="sonata.admin.video" />
<argument>playlist</<argument>
</call>
</service>
```

Overwriting `$parentAssociationMapping` is discouraged.

Deprecated calling of `AbstractAdmin::addChild` without second argument.

UPGRADE FROM 3.33 to 3.34
=========================

Expand Down
27 changes: 4 additions & 23 deletions docs/reference/child_admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ This will create new routes like, for example, ``/playlist/{id}/video/list``,
where the videos will automatically be filtered by post.

To do this, you first need to call the ``addChild`` method in your ``PlaylistAdmin``
service configuration:
service configuration with two arguments, the child admin name (in this case
``VideoAdmin`` service) and the Entity field that relates our child Entity with
its parent:

.. configuration-block::

Expand All @@ -24,31 +26,10 @@ service configuration:
<call method="addChild">
<argument type="service" id="sonata.admin.video" />
<argument>playlist</argument>
</call>
</service>
Then, you have to set the VideoAdmin ``parentAssociationMapping`` attribute to ``playlist`` :

.. code-block:: php
<?php
namespace App\Admin;
// ...
class VideoAdmin extends AbstractAdmin
{
protected $parentAssociationMapping = 'playlist';
// OR
public function getParentAssociationMapping()
{
return 'playlist';
}
}
To display the ``VideoAdmin`` extend the menu in your ``PlaylistAdmin``
class::

Expand Down
48 changes: 44 additions & 4 deletions src/Admin/AbstractAdmin.php
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,9 @@ abstract class AbstractAdmin implements AdminInterface, DomainObjectInterface, A
protected $baseCodeRoute = '';

/**
* The related parent association, ie if OrderElement has a parent property named order,
* then the $parentAssociationMapping must be a string named `order`.
* NEXT_MAJOR: should be default array and private.
*
* @var string
* @var string|array
*/
protected $parentAssociationMapping = null;

Expand Down Expand Up @@ -849,13 +848,40 @@ public function buildDatagrid()
* Returns the name of the parent related field, so the field can be use to set the default
* value (ie the parent object) or to filter the object.
*
* @return string the name of the parent related field
* @throws \InvalidArgumentException
*
* @return null|string
*/
public function getParentAssociationMapping()
{
// NEXT_MAJOR: remove array check
if (\is_array($this->parentAssociationMapping) && $this->getParent()) {
$parent = $this->getParent()->getCode();

if (array_key_exists($parent, $this->parentAssociationMapping)) {
return $this->parentAssociationMapping[$parent];
}

throw new \InvalidArgumentException(sprintf(
"There's no association between %s and %s.",
$this->getCode(),
$this->getParent()->getCode()
));
}

// NEXT_MAJOR: remove this line
return $this->parentAssociationMapping;
}

/**
* @param string $code
* @param string $value
*/
final public function addParentAssociationMapping($code, $value)
{
$this->parentAssociationMapping[$code] = $value;
}

/**
* Returns the baseRoutePattern used to generate the routing information.
*
Expand Down Expand Up @@ -1737,6 +1763,20 @@ public function addChild(AdminInterface $child)
$this->children[$child->getCode()] = $child;

$child->setParent($this);

// NEXT_MAJOR: remove $args and add $field parameter to this function on next Major

$args = \func_get_args();

if (isset($args[1])) {
$child->addParentAssociationMapping($this->getCode(), $args[1]);
} else {
@trigger_error(
'Calling "addChild" without second argument is deprecated since 3.x'
.' and will not be allowed in 4.0.',
E_USER_DEPRECATED
);
}
}

public function hasChild($code)
Expand Down
32 changes: 19 additions & 13 deletions tests/Admin/AdminTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,15 @@ public function testChildren()
$this->assertFalse($postAdmin->hasChild('comment'));

$commentAdmin = new CommentAdmin('sonata.post.admin.comment', 'Application\Sonata\NewsBundle\Entity\Comment', 'SonataNewsBundle:CommentAdmin');
$postAdmin->addChild($commentAdmin);
$postAdmin->addChild($commentAdmin, 'post');

$this->assertTrue($postAdmin->hasChildren());
$this->assertTrue($postAdmin->hasChild('sonata.post.admin.comment'));

$this->assertSame('sonata.post.admin.comment', $postAdmin->getChild('sonata.post.admin.comment')->getCode());
$this->assertSame('sonata.post.admin.post|sonata.post.admin.comment', $postAdmin->getChild('sonata.post.admin.comment')->getBaseCodeRoute());
$this->assertSame($postAdmin, $postAdmin->getChild('sonata.post.admin.comment')->getParent());
$this->assertSame('post', $commentAdmin->getParentAssociationMapping());

$this->assertFalse($postAdmin->isChild());
$this->assertTrue($commentAdmin->isChild());
Expand Down Expand Up @@ -513,6 +515,8 @@ public function testGetBaseRouteName($objFqn, $expected)
}

/**
* @group legacy
* @expectedDeprecation Calling "addChild" without second argument is deprecated since 3.x and will not be allowed in 4.0.
* @dataProvider provideGetBaseRouteName
*/
public function testGetBaseRouteNameWithChildAdmin($objFqn, $expected)
Expand Down Expand Up @@ -544,13 +548,14 @@ public function testGetBaseRouteNameWithChildAdmin($objFqn, $expected)
$commentAdmin->setRouteGenerator($routeGenerator);
$commentAdmin->initialize();

$postAdmin->addChild($commentAdmin);
$postAdmin->addChild($commentAdmin, 'post');

$commentVoteAdmin = new CommentVoteAdmin(
'sonata.post.admin.comment_vote',
'Application\Sonata\NewsBundle\Entity\CommentVote',
'SonataNewsBundle:CommentVoteAdmin'
);

$container->set('sonata.post.admin.comment_vote', $commentVoteAdmin);
$commentVoteAdmin->setConfigurationPool($pool);
$commentVoteAdmin->setRouteBuilder($pathInfo);
Expand All @@ -574,6 +579,7 @@ public function testGetBaseRouteNameWithChildAdmin($objFqn, $expected)
$this->assertTrue($postAdmin->hasRoute('sonata.post.admin.comment|sonata.post.admin.comment_vote.list'));
$this->assertFalse($postAdmin->hasRoute('sonata.post.admin.post|sonata.post.admin.comment.edit'));
$this->assertFalse($commentAdmin->hasRoute('edit'));
$this->assertSame('post', $commentAdmin->getParentAssociationMapping());

/*
* Test the route name from request
Expand Down Expand Up @@ -2274,8 +2280,8 @@ public function testCircularChildAdmin()
'Application\Sonata\NewsBundle\Entity\Comment',
'SonataNewsBundle:CommentAdmin'
);
$postAdmin->addChild($commentAdmin);
$commentAdmin->addChild($postAdmin);
$postAdmin->addChild($commentAdmin, 'post');
$commentAdmin->addChild($postAdmin, 'comment');
}

public function testCircularChildAdminTripleLevel()
Expand All @@ -2300,9 +2306,9 @@ public function testCircularChildAdminTripleLevel()
'Application\Sonata\NewsBundle\Entity\CommentVote',
'SonataNewsBundle:CommentVoteAdmin'
);
$postAdmin->addChild($commentAdmin);
$commentAdmin->addChild($commentVoteAdmin);
$commentVoteAdmin->addChild($postAdmin);
$postAdmin->addChild($commentAdmin, 'post');
$commentAdmin->addChild($commentVoteAdmin, 'comment');
$commentVoteAdmin->addChild($postAdmin, 'post');
}

public function testCircularChildAdminWithItself()
Expand Down Expand Up @@ -2342,13 +2348,13 @@ public function testGetRootAncestor()
$this->assertSame($commentAdmin, $commentAdmin->getRootAncestor());
$this->assertSame($commentVoteAdmin, $commentVoteAdmin->getRootAncestor());

$postAdmin->addChild($commentAdmin);
$postAdmin->addChild($commentAdmin, 'post');

$this->assertSame($postAdmin, $postAdmin->getRootAncestor());
$this->assertSame($postAdmin, $commentAdmin->getRootAncestor());
$this->assertSame($commentVoteAdmin, $commentVoteAdmin->getRootAncestor());

$commentAdmin->addChild($commentVoteAdmin);
$commentAdmin->addChild($commentVoteAdmin, 'comment');

$this->assertSame($postAdmin, $postAdmin->getRootAncestor());
$this->assertSame($postAdmin, $commentAdmin->getRootAncestor());
Expand Down Expand Up @@ -2377,13 +2383,13 @@ public function testGetChildDepth()
$this->assertSame(0, $commentAdmin->getChildDepth());
$this->assertSame(0, $commentVoteAdmin->getChildDepth());

$postAdmin->addChild($commentAdmin);
$postAdmin->addChild($commentAdmin, 'post');

$this->assertSame(0, $postAdmin->getChildDepth());
$this->assertSame(1, $commentAdmin->getChildDepth());
$this->assertSame(0, $commentVoteAdmin->getChildDepth());

$commentAdmin->addChild($commentVoteAdmin);
$commentAdmin->addChild($commentVoteAdmin, 'comment');

$this->assertSame(0, $postAdmin->getChildDepth());
$this->assertSame(1, $commentAdmin->getChildDepth());
Expand All @@ -2408,8 +2414,8 @@ public function testGetCurrentLeafChildAdmin()
'SonataNewsBundle:CommentVoteAdmin'
);

$postAdmin->addChild($commentAdmin);
$commentAdmin->addChild($commentVoteAdmin);
$postAdmin->addChild($commentAdmin, 'post');
$commentAdmin->addChild($commentVoteAdmin, 'comment');

$this->assertNull($postAdmin->getCurrentLeafChildAdmin());
$this->assertNull($commentAdmin->getCurrentLeafChildAdmin());
Expand Down

0 comments on commit d0d9b3d

Please sign in to comment.