Création d'une wishlist avec SyliusResourceBundle
Dans ce tutoriel nous allons créer une entity wishlist, et nous allons ajouter des produits sur celle-ci. Nous verrons de cette façon comment utiliser SyliusResourceBundle pour résoudre notre problématique. Pour ajouter un article l'utilisateur devra être connecté et pour rester concis dans le tutoriel nous ferons une page qui liste les produits de la wishlist et une action qui ajoute un produit à la liste.
Commencons en créant notre entité src/Entity/WishList.php
<?php
namespace App\Entity;
use App\Entity\Product\Product;
use App\Entity\User\ShopUser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Sylius\Component\Resource\Model\ResourceInterface;
/**
* @ORM\Entity
*/
class WishList implements WishListInterface
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\OneToOne(targetEntity="App\Entity\User\ShopUser", cascade={"persist", "remove"})
*/
private $user;
/**
* @ORM\ManyToMany(targetEntity="App\Entity\Product\Product")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getUser(): ?ShopUser
{
return $this->user;
}
public function setUser(?ShopUser $user): self
{
$this->user = $user;
return $this;
}
/**
* @return Collection|Product[]
*/
public function getProducts(): Collection
{
return $this->products;
}
public function addProduct(Product $product): self
{
if (!$this->products->contains($product)) {
$this->products[] = $product;
}
return $this;
}
public function removeProduct(Product $product): self
{
if ($this->products->contains($product)) {
$this->products->removeElement($product);
}
return $this;
}
}
Ainsi que son interface src/Entity/WishListInterface.php
<?php
namespace App\Entity;
use App\Entity\Product\Product;
use App\Entity\User\ShopUser;
use Doctrine\Common\Collections\Collection;
use Sylius\Component\Resource\Model\ResourceInterface;
interface WishListInterface extends ResourceInterface
{
public function getUser(): ?ShopUser;
public function setUser(?ShopUser $user): \App\Entity\WishList;
/**
* @return Collection|Product[]
*/
public function getProducts(): Collection;
public function addProduct(Product $product): \App\Entity\WishList;
public function removeProduct(Product $product): \App\Entity\WishList;
}
Notre interface hérite de ResourceInterface
Nous pouvons mettre à jour la base de données avec php bin/console doctrine:migrations:diff
et php bin/console doctrine:migrations:migrate
Nous déclarons maintenant notre resource dans config/packages/sylius_resource.yaml
sylius_resource:
resources:
app.wishlist:
classes:
model: App\Entity\WishList
Maintenant si on se rend sur notre page /wishlists
on obtient une erreur qui nous indique qu'il ne trouve pas la vue pour l'action index
Unable to find template "/index.html.twig" (looked into: ...).
On va indiquer d'utiliser nos templates, pour ça il faut modifier notre ressource indiquer où sont nos templates :
app_wishlist:
resource: |
alias: app.wishlist
templates: "Wishlist" #ici on indique de regarder dans le dossier templates/Wishlist
type: sylius.resource
Dans le dossier il cherchera les vues :
- Wishlist:show.html.twig
- Wishlist:index.html.twig
- Wishlist:create.html.twig
- Wishlist:update.html.twig
Pour commencer on va juste créer la vue templates/Wishlist/index.html.twig
avec le contenu suivant :
{% extends '@SyliusShop/layout.html.twig' %}
{% block content %}
<h1>index wishlist</h1>
{% endblock %}
On souhaite afficher la liste de notre wishlist (pour le moment vide)
Pour connaitre les variables qui sont a notre disposition avec twig on peut simplement mettre dans la vue {{ dump() }}
et il nous retourne tout ce qui est disponible
la variable wishlists
nous retourne la liste des entrées sur l'entité, on reviendra sur notre vue pour boucler dessus.
Maintenant on va créer un bouton qu'on va rajouter sur une page produit qui nous ajoutera le produit à notre wishlist
Pour ça on se rend dans config/routes.yaml
app_wishlist_add:
path: /wishlist/add/{id}
methods: [POST]
defaults:
_controller: App\Controller\AddWishListAction
On créer notre action src/Controller/AddWishListAction.php
<?php
namespace App\Controller;
use Doctrine\Common\Persistence\ObjectManager;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class AddWishListAction
{
/** @var RepositoryInterface */
private $wishListRepository;
/** @var RepositoryInterface */
private $productRepository;
/** @var FactoryInterface */
private $wishListFactory;
/** @var ObjectManager */
private $objectManager;
/** @var TokenStorageInterface */
private $tokenStorage;
/** @var UrlGeneratorInterface */
private $router;
public function __construct(RepositoryInterface $wishListRepository,
RepositoryInterface $productRepository,
FactoryInterface $wishListFactory,
ObjectManager $objectManager,
TokenStorageInterface $tokenStorage,
UrlGeneratorInterface $router)
{
$this->wishListRepository = $wishListRepository;
$this->productRepository = $productRepository;
$this->wishListFactory = $wishListFactory;
$this->objectManager = $objectManager;
$this->tokenStorage = $tokenStorage;
$this->router = $router;
}
public function __invoke(Request $request, Session $session): Response
{
$productId = $request->attributes->getInt('id');
$product = $this->productRepository->find($productId);
$tokenUser = $this->tokenStorage->getToken();
if (!is_null($tokenUser) && $tokenUser == 'anon.') {
$session->getFlashBag()->add('error', 'You have to be logged to add to the wishlist');
$productRoute = $this->router->generate('sylius_shop_product_show',
array('slug' => $product->getSlug())
);
return new RedirectResponse($productRoute);
}
$user = $tokenUser->getUser();
$wishList = $this->wishListRepository->findOneBy(array('user' => $user->getId()));
if (is_null($wishList)) {
$wishList = $this->wishListFactory->createNew();
$wishList->setUser($user);
}
$wishList->addProduct($product);
$this->objectManager->persist($wishList);
$this->objectManager->flush();
$session->getFlashBag()->add('success', 'Product added to the wishlist');
$productRoute = $this->router->generate('sylius_shop_product_show',
array('slug' => $product->getSlug())
);
return new RedirectResponse($productRoute);
}
}
Ensuite on la déclare comme service config/services.yaml
services:
#...
App\Controller\AddWishListAction:
arguments:
- '@app.repository.wishlist'
- '@sylius.repository.product'
- '@app.factory.wishlist'
- '@app.manager.wishlist'
- '@security.token_storage'
public: true
Puis on créer la vue du bouton templates/Wishlist/_addWishList.html.twig
{% if app.user is not null %}
<form action="{{ path('app_wishlist_add', {id: product.id }) }}" method="post">
<button type="submit" class="ui huge red icon button"><i class="heart icon"></i></button>
</form>
{% endif %}
On ajoute notre bouton sur les pages produits, on va utiliser les evenements des rendus des vues, pour ça on se rend dans le fichier config/packages/sylius_ui.yaml
et on ajoute :
sylius_ui:
events:
sylius.shop.product.show.right_sidebar:
blocks:
add_wishlist: 'Wishlist/_addWishList.html.twig'
Maintenant, quand on se rend sur une page produit nous avons le résultat suivant :
Ajoutez deux ou trois produits à votre wishlist
Maintenant pour retourner uniquement les produits de l'utilisateur connecté il va nous falloir surcharger le repository.
On créer donc notre repository src/Repository/WishlistRepository.php
<?php
namespace App\Repository;
use App\Entity\WishList;
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
class WishlistRepository extends EntityRepository implements WishListRepositoryInterface
{
public function findProductInWishList($userId): ?WishList
{
return $this->createQueryBuilder('w')
->innerJoin('w.user', 'u', 'WHERE', 'u.id = :userId')
->setParameter('userId', $userId->getId())
->getQuery()
->getOneOrNullResult()
;
}
}
Son interface src/Repository/WishlistRepositoryInterface.php
<?php
namespace App\Repository;
use App\Entity\WishList;
use Sylius\Component\Resource\Repository\RepositoryInterface;
interface WishlistRepositoryInterface extends RepositoryInterface
{
public function findProductInWishList($user): ?WishList;
}
On indique notre nouveau repository à notre resource, config/packages/resources.yaml
sylius_resource:
resources:
app.wishlist:
classes:
model: App\Entity\WishList
repository: App\Repository\WishlistRepository # on rajoute notre repository ici
Et enfin on crée notre route dans config/routes.yaml
app_wishlist_index:
path: /wishlists
methods: [GET]
defaults:
_controller: app.controller.wishlist:indexAction
_sylius:
template: Wishlist/index.html.twig
repository:
method: findProductInWishList
arguments:
- "expr:service('sylius.context.customer').getCustomer()"
On indique la méthode qu'on veut utiliser, donc la méthode findProductInWishList
qui demande comme argument l'utilisateur en cours, et on l'envoie grâce au service de Sylius : expr:service('sylius.context.customer').getCustomer()
Enfin, on va modifer notre vue index afin d'afficher nos articles templates/Wishlist/index.html.twig
{% extends '@SyliusShop/layout.html.twig' %}
{% block content %}
<h1>index wishlist</h1>
<div class="ui four doubling cards">
{% for product in wishlists.products %}
{% include '@SyliusShop/Product/_box.html.twig' %}
{% endfor %}
</div>
{% endblock %}
Et on peut se rendre sur la page /wishlists
pour retrouver nos articles de notre wishlist :
Voilà !