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

[Docs] Add docs for custom entity with access per admin channel #12619

Merged
Merged
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
261 changes: 261 additions & 0 deletions docs/cookbook/entities/custom-model-accessible-for-channel-admin.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
.. rst-class:: plus-doc

How to add a custom model accessible for respective channel administrators?
===========================================================================

Given that you are using Sylius Plus, the licensed edition of Sylius, you may have
the Administrators per Channel defined in your application. Thus when you add a new,
channel-based entity to it, you will need to enable this entity to be viewed only by the relevant channel admins.

1. Define your custom model, our example will be the **Supplier entity**
------------------------------------------------------------------------

In order to prepare a simple Entity follow :doc:`this guide </cookbook/entities/custom-model>`.

Remember to then add your entity to the admin menu. Adding a new entity to the admin menu
is described in the section ``How to customize Admin Menu`` of :doc:`this guide </customization/menu>`.

* Having your Supplier entity created, add a channel field with relation to the ``Channel`` entity:

.. code-block:: php

/**
* @var ChannelInterface
* @ORM\ManyToOne(targetEntity="Sylius\Plus\Entity\ChannelInterface")
* @ORM\JoinColumn(name="channel_id", referencedColumnName="id", nullable=true)
*/
protected $channel;

* Create a proper migration for these changes.

* Next, create a form type for your entity:

.. code-block:: php

<?php

declare(strict_types=1);

namespace App\Form;

use Sylius\Bundle\ChannelBundle\Form\Type\ChannelChoiceType;
use Sylius\Bundle\ResourceBundle\Form\Type\AbstractResourceType;
use Sylius\Plus\ChannelAdmin\Application\Provider\AvailableChannelsForAdminProviderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

final class SupplierType extends AbstractResourceType
{
/** @var AvailableChannelsForAdminProviderInterface */
private $availableChannelsForAdminProvider;

public function __construct(
AvailableChannelsForAdminProviderInterface $availableChannelsForAdminProvider,
string $dataClass,
array $validationGroups = []
) {
parent::__construct($dataClass, $validationGroups);

$this->availableChannelsForAdminProvider = $availableChannelsForAdminProvider;
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class, [
'label' => 'Name'
])
->add('channel', ChannelChoiceType::class, [
'choices' => $this->availableChannelsForAdminProvider->getChannels(),
'label' => 'sylius.ui.channel',
])
;
}

public function getBlockPrefix(): string
{
return 'supplier';
}
}

.. code-block:: yaml

App\Form\SupplierType:
arguments: ['@Sylius\Plus\ChannelAdmin\Application\Provider\AvailableChannelsForAdminProviderInterface', 'App\Entity\Supplier', ['sylius']]
tags: ['form.type']

The ``Sylius\Plus\ChannelAdmin\Application\Provider\AvailableChannelsForAdminProviderInterface`` service allows getting a list of proper channels for the currently logged-in admin.

Remember to register ``App\Form\SupplierType`` for resource:

.. code-block:: yaml

sylius_resource:
resources:
app.supplier:
driver: doctrine/orm
classes:
model: App\Entity\Supplier
+ form: App\Form\SupplierType

1. Restrict access to the entity for the respective channel administrator roles (using ACL/RBAC):
-------------------------------------------------------------------------------------------------

.. note::

More information about using administrator roles (ACL/RBAC) can be found :doc:`here </book/customers/admin_user>`.

* Create ``App\Checker\ResourceChannelEnabilibityChecker`` and decorate ``Sylius\Plus\ChannelAdmin\Application\Checker\ResourceChannelEnabilibityCheckerInterface``.

* Then add ``Supplier`` as checking resource:

.. code-block:: php

<?php

declare(strict_types=1);

namespace App\Checker;

use Sylius\Plus\ChannelAdmin\Application\Checker\ResourceChannelEnabilibityCheckerInterface;
use Sylius\Plus\ChannelAdmin\Application\Checker\ResourceChannelEnabilibityChecker as DecoratedResourceChannelEnabilibityChecker;

final class ResourceChannelEnabilibityChecker implements ResourceChannelEnabilibityCheckerInterface
{
/** @var ResourceChannelEnabilibityCheckerInterface */
private $decoratedResourceChannelEnabilibityChecker;

public function __construct(ResourceChannelEnabilibityCheckerInterface $decoratedResourceChannelEnabilibityChecker)
{
$this->decoratedResourceChannelEnabilibityChecker = $decoratedResourceChannelEnabilibityChecker;
}

public function forResourceName(string $resourceName): bool
{
if ($this->decoratedResourceChannelEnabilibityChecker->forResourceName($resourceName)) {
return true;
}

return $resourceName === 'supplier';
}
}

.. code-block:: yaml

App\Checker\ResourceChannelEnabilibityChecker:
decorates: Sylius\Plus\ChannelAdmin\Application\Checker\ResourceChannelEnabilibityCheckerInterface
arguments: ['@.inner']

* Create ``App\Checker\ResourceChannelChecker`` and decorate ``Sylius\Plus\ChannelAdmin\Application\Checker\ResourceChannelCheckerInterface`` next add condition for checking ``Supplier``.

.. code-block:: php

<?php

declare(strict_types=1);

namespace App\Checker;

use Sylius\Plus\ChannelAdmin\Application\Checker\ResourceChannelCheckerInterface;
use Sylius\Plus\Entity\ChannelInterface;

final class ResourceChannelChecker implements ResourceChannelCheckerInterface
{
/** @var ResourceChannelCheckerInterface */
private $decoratedResourceChannelChecker;

public function __construct(ResourceChannelCheckerInterface $decoratedResourceChannelChecker)
{
$this->decoratedResourceChannelChecker = $decoratedResourceChannelChecker;
}

public function isFromChannel(object $resource, ChannelInterface $channel): bool
{
if (
$resource instanceof Supplier && in_array($resource->getChannel(), [$channel, null], true)
) {
return true;
}

return $this->decoratedResourceChannelChecker->isFromChannel($resource, $channel);
}
}

.. code-block:: yaml

App\Checker\ResourceChannelChecker:
decorates: Sylius\Plus\ChannelAdmin\Application\Checker\ResourceChannelCheckerInterface
arguments: ['@.inner']

After that, access to the resource should work properly with all restrictions.

* Next add ``RestrictingSupplierListQueryBuilder``:

.. code-block:: php

<?php

declare(strict_types=1);

namespace App\Doctrine\ORM;

use Doctrine\ORM\QueryBuilder;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
use Sylius\Component\Core\Model\ChannelInterface;
use Sylius\Plus\ChannelAdmin\Application\Provider\AdminChannelProviderInterface;

final class RestrictingSupplierListQueryBuilder
{
/** @var AdminChannelProviderInterface */
private $adminChannelProvider;

/** @var EntityRepository */
private $supplierRepository;

public function __construct(
AdminChannelProviderInterface $adminChannelProvider,
EntityRepository $supplierRepository
) {
$this->adminChannelProvider = $adminChannelProvider;
$this->supplierRepository = $supplierRepository;
}

public function create(): QueryBuilder
{
$listQueryBuilder = $this->supplierRepository->createQueryBuilder('o');

/** @var ChannelInterface|null $channel */
$channel = $this->adminChannelProvider->getChannel();
if ($channel === null) {
return $listQueryBuilder;
}

return $listQueryBuilder
->andWhere('o.channel = :channel')
->setParameter('channel', $channel)
;
}
}

.. code-block:: yaml

App\Doctrine\ORM\RestrictingSupplierListQueryBuilder:
public: true
class: App\Doctrine\ORM\RestrictingSupplierListQueryBuilder
arguments: ['@Sylius\Plus\ChannelAdmin\Application\Provider\AdminChannelProviderInterface', '@app.repository.supplier']

* Add method to the Suppliers grid:

.. code-block:: yaml

sylius_grid:
grids:
app_admin_supplier:
driver:
name: doctrine/orm
options:
class: App\Entity\Supplier
repository:
+ method: [expr:service('App\\Doctrine\\ORM\\RestrictingSupplierListQueryBuilder'), create]

Well done! That's it, now you have a Supplier entity, that is accessible within the Sylius Plus Administrators per Channel feature!
1 change: 1 addition & 0 deletions docs/cookbook/entities/map.rst.inc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
* :doc:`/cookbook/entities/custom-model`
* :doc:`/cookbook/entities/custom-model-accessible-for-channel-admin`
* :doc:`/cookbook/entities/custom-translatable-model`
1 change: 1 addition & 0 deletions docs/cookbook/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Entities
:hidden:

entities/custom-model
entities/custom-model-accessible-for-channel-admin
entities/custom-translatable-model

.. include:: /cookbook/entities/map.rst.inc
Expand Down