From 792fee5e79e950b481a0541f25b7b9c268dc9fb9 Mon Sep 17 00:00:00 2001 From: Maximilien Gilet Date: Thu, 21 Jul 2016 20:22:49 +0200 Subject: [PATCH 1/6] [~] ORM agnostic entities --- Controller/NotificationController.php | 4 ++-- .../MgiletNotificationExtension.php | 5 ++-- {Model => Entity}/AbstractNotification.php | 13 +---------- .../UserNotificationInterface.php | 2 +- Manager/NotificationManager.php | 4 ++-- MgiletNotificationBundle.php | 2 +- README.md | 23 +------------------ .../doctrine/AbstractNotification.orm.xml | 18 +++++++++++++++ Resources/doc/index.rst | 6 ++--- 9 files changed, 32 insertions(+), 45 deletions(-) rename {Model => Entity}/AbstractNotification.php (83%) rename {Model => Entity}/UserNotificationInterface.php (94%) create mode 100755 Resources/config/doctrine/AbstractNotification.orm.xml diff --git a/Controller/NotificationController.php b/Controller/NotificationController.php index 65c0548..d553cdd 100755 --- a/Controller/NotificationController.php +++ b/Controller/NotificationController.php @@ -2,8 +2,8 @@ namespace Mgilet\NotificationBundle\Controller; -use Mgilet\NotificationBundle\Model\AbstractNotification; -use Mgilet\NotificationBundle\Model\UserNotificationInterface; +use Mgilet\NotificationBundle\Entity\AbstractNotification; +use Mgilet\NotificationBundle\Entity\UserNotificationInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; diff --git a/DependencyInjection/MgiletNotificationExtension.php b/DependencyInjection/MgiletNotificationExtension.php index c53f939..280c30a 100755 --- a/DependencyInjection/MgiletNotificationExtension.php +++ b/DependencyInjection/MgiletNotificationExtension.php @@ -22,12 +22,13 @@ public function load(array $configs, ContainerBuilder $container) { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); - $loader = new YamlFileLoader( + $yamlLoader = new YamlFileLoader( $container, new FileLocator(__DIR__.'/../Resources/config') ); - $loader->load('services.yml'); + $yamlLoader->load('services.yml'); $container->setParameter('mgilet_notification.notification_class', $config['notification_class']); } + } diff --git a/Model/AbstractNotification.php b/Entity/AbstractNotification.php similarity index 83% rename from Model/AbstractNotification.php rename to Entity/AbstractNotification.php index b81518d..8729501 100755 --- a/Model/AbstractNotification.php +++ b/Entity/AbstractNotification.php @@ -1,52 +1,41 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst index 49030ab..e3a6da2 100755 --- a/Resources/doc/index.rst +++ b/Resources/doc/index.rst @@ -83,7 +83,7 @@ Sample configuration: ... use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; - use Mgilet\NotificationBundle\Model\UserNotificationInterface; + use Mgilet\NotificationBundle\Entity\UserNotificationInterface; /** * @ORM\Entity @@ -157,7 +157,7 @@ Sample configuration: Now we need the Notification class. -Simply extend the provided MappedSuperClass ``AbstractNotification`` class (from the ``Model`` folder) and link it to the ``User`` entity. +Simply extend the provided MappedSuperClass ``AbstractNotification`` class (from the ``Entity`` folder) and link it to the ``User`` entity. Here is a sample configuration: @@ -170,7 +170,7 @@ Here is a sample configuration: namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; - use Mgilet\NotificationBundle\Model\AbstractNotification; + use Mgilet\NotificationBundle\Entity\AbstractNotification; /** * @ORM\Entity From 43341e062c58ff985aed2a512b48159bf68c0dff Mon Sep 17 00:00:00 2001 From: Maximilien Gilet Date: Thu, 21 Jul 2016 20:33:52 +0200 Subject: [PATCH 2/6] [~] SensiolabInsight best practices --- Resources/config/doctrine/AbstractNotification.orm.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/config/doctrine/AbstractNotification.orm.xml b/Resources/config/doctrine/AbstractNotification.orm.xml index 4ddd694..06e1602 100755 --- a/Resources/config/doctrine/AbstractNotification.orm.xml +++ b/Resources/config/doctrine/AbstractNotification.orm.xml @@ -15,4 +15,4 @@ - \ No newline at end of file + From 643ba71625a8361e6212f045af3541425e554d30 Mon Sep 17 00:00:00 2001 From: Maximilien Gilet Date: Mon, 25 Jul 2016 16:55:59 +0200 Subject: [PATCH 3/6] [+] Event dispatch --- Event/NotificationEvent.php | 30 +++++++++++ Manager/NotificationManager.php | 90 +++++++++++++++++++++++++++++++-- MgiletNotificationEvents.php | 40 +++++++++++++++ Resources/doc/usage.rst | 12 +++++ 4 files changed, 167 insertions(+), 5 deletions(-) create mode 100644 Event/NotificationEvent.php create mode 100644 MgiletNotificationEvents.php diff --git a/Event/NotificationEvent.php b/Event/NotificationEvent.php new file mode 100644 index 0000000..d5eceba --- /dev/null +++ b/Event/NotificationEvent.php @@ -0,0 +1,30 @@ +notification = $notification; + } + + /** + * @return AbstractNotification + */ + public function getNotification() + { + return $this->notification; + } + +} diff --git a/Manager/NotificationManager.php b/Manager/NotificationManager.php index cbda20d..fedfb41 100755 --- a/Manager/NotificationManager.php +++ b/Manager/NotificationManager.php @@ -5,6 +5,9 @@ use Doctrine\ORM\EntityManager; use Mgilet\NotificationBundle\Entity\AbstractNotification; use Mgilet\NotificationBundle\Entity\UserNotificationInterface; +use Mgilet\NotificationBundle\Event\NotificationEvent; +use Mgilet\NotificationBundle\MgiletNotificationEvents; +use Symfony\Component\EventDispatcher\EventDispatcher; /** * Class NotificationManager @@ -17,6 +20,7 @@ class NotificationManager private $om; private $notification; private $repository; + private $dispatcher; /** * NotificationManager constructor. @@ -29,6 +33,7 @@ public function __construct(EntityManager $om, $notification) $this->om = $om; $this->notification = $notification; $this->repository = $om->getRepository($notification); + $this->dispatcher = new EventDispatcher(); } /** @@ -56,6 +61,10 @@ public function generateNotification($subject, $message = null, $link = null) ->setSubject($subject) ->setMessage($message) ->setLink($link); + $this->om->persist($notification); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::onCreatedNotification, $event); return $notification; } @@ -71,8 +80,10 @@ public function generateNotification($subject, $message = null, $link = null) public function addNotification(UserNotificationInterface $user, AbstractNotification $notification) { $user->addNotification($notification); - $this->om->persist($user); - $this->om->flush(); + $this->om->flush($notification); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::onNewNotification, $event); return $notification; } @@ -99,8 +110,10 @@ public function createNotification(UserNotificationInterface $user, $subject, $m public function removeNotification(AbstractNotification $notification) { $this->om->remove($notification); - $this->om->persist($notification); $this->om->flush(); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::onRemovedNotification, $event); } /** @@ -113,8 +126,10 @@ public function removeNotification(AbstractNotification $notification) public function markAsSeen(AbstractNotification $notification) { $notification->setSeen(true); - $this->om->persist($notification); - $this->om->flush(); + $this->om->flush($notification); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::onSeenNotification, $event); return $notification; } @@ -184,4 +199,69 @@ public function getUnseenNotificationCount($user) )); } + /** + * @param AbstractNotification $notification + * @param \DateTime $date + * @return AbstractNotification + */ + public function setNotificationDate(AbstractNotification $notification, \DateTime $date) + { + $notification->setDate($date); + $this->om->flush($notification); + + return $notification; + } + + /** + * @param AbstractNotification $notification + * @param string $subject + * @return AbstractNotification + */ + public function setNotificationSubject(AbstractNotification $notification, $subject) + { + $notification->setSubject($subject); + $this->om->flush($notification); + + return $notification; + } + + /** + * @param AbstractNotification $notification + * @param string $message + * @return AbstractNotification + */ + public function setNotificationMessage(AbstractNotification $notification, $message) + { + $notification->setMessage($message); + $this->om->flush($notification); + + return $notification; + } + + /** + * @param AbstractNotification $notification + * @param string $link + * @return AbstractNotification + */ + public function setNotificationLink(AbstractNotification $notification, $link) + { + $notification->setLink($link); + $this->om->flush($notification); + + return $notification; + } + + /** + * @param AbstractNotification $notification + * @param boolean $seen + * @return AbstractNotification + */ + public function setNotificationSeen(AbstractNotification $notification, $seen) + { + $notification->setLink($seen); + $this->om->flush($notification); + + return $notification; + } + } diff --git a/MgiletNotificationEvents.php b/MgiletNotificationEvents.php new file mode 100644 index 0000000..78ed4ef --- /dev/null +++ b/MgiletNotificationEvents.php @@ -0,0 +1,40 @@ + Date: Mon, 16 Oct 2017 16:39:28 +0200 Subject: [PATCH 4/6] Bundle refactored from scratch --- Annotation/Notifiable.php | 44 ++ Controller/NotificationController.php | 92 +-- DependencyInjection/Configuration.php | 39 -- .../MgiletNotificationExtension.php | 3 - Entity/NotifiableEntity.php | 147 +++++ Entity/NotifiableNotification.php | 120 ++++ ...tractNotification.php => Notification.php} | 59 +- .../NotifiableNotificationRepository.php | 134 ++++ Entity/Repository/NotifiableRepository.php | 60 ++ Entity/Repository/NotificationRepository.php | 48 ++ Entity/UserNotificationInterface.php | 40 -- Event/NotificationEvent.php | 21 +- Manager/NotificationManager.php | 621 ++++++++++++++---- MgiletNotificationEvents.php | 37 +- NotifiableDiscovery.php | 88 +++ NotifiableInterface.php | 15 + .../doctrine/AbstractNotification.orm.xml | 18 - Resources/config/routing.yml | 3 + Resources/config/services.yml | 9 +- Resources/doc/advanced-configuration.rst | 67 -- Resources/doc/further.rst | 6 - Resources/doc/index.rst | 208 +----- Resources/doc/overriding.rst | 66 -- Resources/doc/usage.rst | 114 ++-- Resources/public/css/mgilet_notification.css | 54 -- Resources/public/js/ajax-notification.js | 69 -- .../views/notification_dropdown.html.twig | 47 -- Resources/views/notification_list.html.twig | 4 +- Resources/views/notifications.html.twig | 24 +- Twig/NotificationExtension.php | 177 +++-- composer.json | 2 +- 31 files changed, 1513 insertions(+), 923 deletions(-) create mode 100644 Annotation/Notifiable.php delete mode 100755 DependencyInjection/Configuration.php create mode 100644 Entity/NotifiableEntity.php create mode 100644 Entity/NotifiableNotification.php rename Entity/{AbstractNotification.php => Notification.php} (54%) create mode 100644 Entity/Repository/NotifiableNotificationRepository.php create mode 100644 Entity/Repository/NotifiableRepository.php create mode 100644 Entity/Repository/NotificationRepository.php delete mode 100755 Entity/UserNotificationInterface.php create mode 100644 NotifiableDiscovery.php create mode 100644 NotifiableInterface.php delete mode 100755 Resources/config/doctrine/AbstractNotification.orm.xml create mode 100644 Resources/config/routing.yml delete mode 100755 Resources/doc/advanced-configuration.rst delete mode 100755 Resources/doc/overriding.rst delete mode 100755 Resources/public/css/mgilet_notification.css delete mode 100755 Resources/public/js/ajax-notification.js delete mode 100755 Resources/views/notification_dropdown.html.twig diff --git a/Annotation/Notifiable.php b/Annotation/Notifiable.php new file mode 100644 index 0000000..1e60d4d --- /dev/null +++ b/Annotation/Notifiable.php @@ -0,0 +1,44 @@ +name; + } + + /** + * @param mixed $name + * + * @return Notifiable + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + +} \ No newline at end of file diff --git a/Controller/NotificationController.php b/Controller/NotificationController.php index d553cdd..d2467ca 100755 --- a/Controller/NotificationController.php +++ b/Controller/NotificationController.php @@ -2,13 +2,12 @@ namespace Mgilet\NotificationBundle\Controller; -use Mgilet\NotificationBundle\Entity\AbstractNotification; -use Mgilet\NotificationBundle\Entity\UserNotificationInterface; +use Mgilet\NotificationBundle\Entity\Notification; +use Mgilet\NotificationBundle\NotifiableInterface; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\Security\Core\Exception\AuthenticationException; /** * Class NotificationController @@ -20,33 +19,44 @@ class NotificationController extends Controller /** * List of all notifications * - * @Route("/", name="notifications_list") + * @Route("/{notifiable}", name="notification_list") * @Method("GET") - * @throws \LogicException + * @param NotifiableInterface $notifiable + * + * @return \Symfony\Component\HttpFoundation\Response */ - public function listAction() + public function listAction($notifiable) { + $notifiableRepo = $this->get('doctrine.orm.entity_manager')->getRepository('MgiletNotificationBundle:NotifiableNotification'); return $this->render('MgiletNotificationBundle::notifications.html.twig', array( - 'notifications' => $this->get('mgilet.notification')->getUserNotifications($this->getUser()) + 'notifiableNotifications' => $notifiableRepo->findAllForNotifiableId($notifiable) )); } /** * Set a Notification as seen * - * @Route("/{notification}/mark_as_seen", name="notification_mark_as_seen") + * @Route("/{notifiable}/mark_as_seen/{notification}", name="notification_mark_as_seen") * @Method("POST") - * @param AbstractNotification $notification + * @param int $notifiable + * @param Notification $notification + * * @return JsonResponse + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\EntityNotFoundException * @throws \LogicException */ - public function markAsSeenAction($notification) + public function markAsSeenAction($notifiable, $notification) { - $em = $this->getDoctrine()->getEntityManager(); - $notification = $this->get('mgilet.notification')->getNotificationById($notification); - $notification->setSeen(true); - $em->persist($notification); - $em->flush(); + $manager = $this->get('mgilet.notification'); + $manager->markAsSeen( + $manager->getNotifiableInterface($manager->getNotifiableEntityById($notifiable)), + $manager->getNotification($notification), + true + ); return new JsonResponse(true); } @@ -54,19 +64,27 @@ public function markAsSeenAction($notification) /** * Set a Notification as unseen * - * @Route("/{notification}/mark_as_unseen", name="notification_mark_as_unseen") + * @Route("/{notifiable}/mark_as_unseen/{notification}", name="notification_mark_as_unseen") * @Method("POST") - * @param AbstractNotification $notification + * @param $notifiable + * @param $notification + * * @return JsonResponse + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\EntityNotFoundException * @throws \LogicException */ - public function markAsUnSeenAction($notification) + public function markAsUnSeenAction($notifiable, $notification) { - $em = $this->getDoctrine()->getEntityManager(); - $notification = $this->get('mgilet.notification')->getNotificationById($notification); - $notification->setSeen(false); - $em->persist($notification); - $em->flush(); + $manager = $this->get('mgilet.notification'); + $manager->markAsUnseen( + $manager->getNotifiableInterface($manager->getNotifiableEntityById($notifiable)), + $manager->getNotification($notification), + true + ); return new JsonResponse(true); } @@ -74,24 +92,22 @@ public function markAsUnSeenAction($notification) /** * Set all Notifications for a User as seen * - * @Route("/markAllAsSeen", name="notification_mark_all_as_seen") + * @Route("/{notifiable}/markAllAsSeen", name="notification_mark_all_as_seen") * @Method("POST") - * @throws \LogicException - * @throws \Symfony\Component\Security\Core\Exception\AuthenticationException + * @param $notifiable + * + * @return JsonResponse + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\OptimisticLockException */ - public function markAllAsSeenAction() + public function markAllAsSeenAction($notifiable) { - $em = $this->getDoctrine()->getEntityManager(); - $user = $this->getUser(); - if (!is_object($user) || !$user instanceof UserNotificationInterface) { - throw new AuthenticationException('This user does not have access to this section.'); - } - $notifications = $this->get('mgilet.notification')->getUnseenUserNotifications($user); - foreach ($notifications as $notification) { - $notification->setSeen(true); - $em->persist($notification); - } - $em->flush(); + $manager = $this->get('mgilet.notification'); + $manager->markAllAsSeen( + $manager->getNotifiableInterface($manager->getNotifiableEntityById($notifiable)), + true + ); return new JsonResponse(true); } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php deleted file mode 100755 index 945c9bf..0000000 --- a/DependencyInjection/Configuration.php +++ /dev/null @@ -1,39 +0,0 @@ -buildConfigTree(); - } - - private function buildConfigTree() - { - - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('mgilet_notification'); - $rootNode - ->children() - ->scalarNode('notification_class') - ->cannotBeEmpty() - ->defaultValue('AppBundle\\Entity\\Notification') - ->info('Entity for a notification (default: AppBundle\\Entity\\Notification)') - ->end(); - - return $treeBuilder; - } -} diff --git a/DependencyInjection/MgiletNotificationExtension.php b/DependencyInjection/MgiletNotificationExtension.php index 280c30a..a7d76d2 100755 --- a/DependencyInjection/MgiletNotificationExtension.php +++ b/DependencyInjection/MgiletNotificationExtension.php @@ -20,15 +20,12 @@ class MgiletNotificationExtension extends Extension */ public function load(array $configs, ContainerBuilder $container) { - $configuration = new Configuration(); - $config = $this->processConfiguration($configuration, $configs); $yamlLoader = new YamlFileLoader( $container, new FileLocator(__DIR__.'/../Resources/config') ); $yamlLoader->load('services.yml'); - $container->setParameter('mgilet_notification.notification_class', $config['notification_class']); } } diff --git a/Entity/NotifiableEntity.php b/Entity/NotifiableEntity.php new file mode 100644 index 0000000..8fc865b --- /dev/null +++ b/Entity/NotifiableEntity.php @@ -0,0 +1,147 @@ +identifier = $identifier; + $this->class = $class; + $this->notifiableNotifications = new ArrayCollection(); + } + + /** + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * @param string $identifier + * + * @return NotifiableEntity + */ + public function setIdentifier($identifier) + { + $this->identifier = $identifier; + + return $this; + } + + /** + * @return string + */ + public function getClass() + { + return $this->class; + } + + /** + * @param string $class + * + * @return NotifiableEntity + */ + public function setClass($class) + { + $this->class = $class; + + return $this; + } + + /** + * @return ArrayCollection|NotifiableNotification[] + */ + public function getNotifiableNotifications() + { + return $this->notifiableNotifications; + } + + /** + * @param NotifiableNotification $notifiableNotification + * + * @return $this + */ + public function addNotifiableNotification(NotifiableNotification $notifiableNotification) + { + if (!$this->notifiableNotifications->contains($notifiableNotification)) { + $this->notifiableNotifications[] = $notifiableNotification; + $notifiableNotification->setNotifiableEntity($this); + } + + return $this; + } + + /** + * @param NotifiableNotification $notifiableNotification + * + * @return $this + */ + public function removeNotifiableNotification(NotifiableNotification $notifiableNotification) + { + if ($this->notifiableNotifications->contains($notifiableNotification)) { + $this->notifiableNotifications->removeElement($notifiableNotification); + $notifiableNotification->setNotifiableEntity(null); + } + + return $this; + } +} \ No newline at end of file diff --git a/Entity/NotifiableNotification.php b/Entity/NotifiableNotification.php new file mode 100644 index 0000000..fdd5d05 --- /dev/null +++ b/Entity/NotifiableNotification.php @@ -0,0 +1,120 @@ +seen = false; + } + + /** + * @return int Notification Id + */ + public function getId() + { + return $this->id; + } + + /** + * @return boolean Seen status of the notification + */ + public function isSeen() + { + return $this->seen; + } + + /** + * @param boolean $isSeen Seen status of the notification + * @return $this + */ + public function setSeen($isSeen) + { + $this->seen = $isSeen; + + return $this; + } + + /** + * @return Notification + */ + public function getNotification() + { + return $this->notification; + } + + /** + * @param Notification $notification + * + * @return NotifiableNotification + */ + public function setNotification($notification) + { + $this->notification = $notification; + + return $this; + } + + /** + * @return NotifiableEntity + */ + public function getNotifiableEntity() + { + return $this->notifiableEntity; + } + + /** + * @param NotifiableEntity $notifiableEntity + * + * @return NotifiableNotification + */ + public function setNotifiableEntity(NotifiableEntity $notifiableEntity = null) + { + $this->notifiableEntity = $notifiableEntity; + + return $this; + } +} \ No newline at end of file diff --git a/Entity/AbstractNotification.php b/Entity/Notification.php similarity index 54% rename from Entity/AbstractNotification.php rename to Entity/Notification.php index 8729501..526372a 100755 --- a/Entity/AbstractNotification.php +++ b/Entity/Notification.php @@ -2,50 +2,67 @@ namespace Mgilet\NotificationBundle\Entity; + +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\Mapping as ORM; + /** * Class AbstractNotification * Notifications defined in your app must implement this class - * @package maximilienGilet\NotificationBundle\Model + * + * @ORM\Table(name="notification") + * @ORM\Entity(repositoryClass="Mgilet\NotificationBundle\Entity\Repository\NotificationRepository") */ -abstract class AbstractNotification +class Notification { /** - * @var int + * @var integer $id + * + * @ORM\Column(type="integer") + * @ORM\Id + * @ORM\GeneratedValue */ protected $id; /** * @var \DateTime + * @ORM\Column(type="datetime") */ protected $date; /** * @var string + * @ORM\Column(type="string", length=255) */ protected $subject; /** * @var string + * @ORM\Column(type="string", length=255, nullable=true) */ protected $message; /** * @var string + * @ORM\Column(type="string", length=255, nullable=true) */ protected $link; /** - * @var boolean + * @var NotifiableNotification[]|ArrayCollection + * @ORM\OneToMany(targetEntity="Mgilet\NotificationBundle\Entity\NotifiableNotification", mappedBy="notification") */ - protected $seen; + protected $notifiableNotifications; + + /** * AbstractNotification constructor. */ public function __construct() { - $this->seen = false; $this->date = new \DateTime(); + $this->notifiableNotifications = new ArrayCollection(); } /** @@ -132,22 +149,40 @@ public function setLink($link) return $this; } + /** + * @return ArrayCollection|NotifiableNotification[] + */ + public function getNotifiableNotifications() + { + return $this->notifiableNotifications; + } /** - * @return boolean Seen status of the notification + * @param NotifiableNotification $notifiableNotification + * + * @return $this */ - public function isSeen() + public function addNotifiableNotification(NotifiableNotification $notifiableNotification) { - return $this->seen; + if (!$this->notifiableNotifications->contains($notifiableNotification)) { + $this->notifiableNotifications[] = $notifiableNotification; + $notifiableNotification->setNotification($this); + } + + return $this; } /** - * @param boolean $isSeen Seen status of the notification + * @param NotifiableNotification $notifiableNotification + * * @return $this */ - public function setSeen($isSeen) + public function removeNotifiableNotification(NotifiableNotification $notifiableNotification) { - $this->seen = $isSeen; + if ($this->notifiableNotifications->contains($notifiableNotification)) { + $this->notifiableNotifications->removeElement($notifiableNotification); + $notifiableNotification->setNotification(null); + } return $this; } diff --git a/Entity/Repository/NotifiableNotificationRepository.php b/Entity/Repository/NotifiableNotificationRepository.php new file mode 100644 index 0000000..8d8b471 --- /dev/null +++ b/Entity/Repository/NotifiableNotificationRepository.php @@ -0,0 +1,134 @@ +createQueryBuilder('nn') + ->join('nn.notification', 'n') + ->join('nn.notifiableEntity', 'ne') + ->where('n.id = :notification_id') + ->andWhere('ne.id = :notifiable_id') + ->setParameter('notification_id', $notification_id) + ->setParameter('notifiable_id', $notifiable_id) + ->getQuery() + ->getOneOrNullResult() + ; + } + + /** + * Get all NotifiableNotifications for a notifiable + * + * @param $notifiable_identifier + * @param $notifiable_class + * + * @return NotifiableNotification[] + */ + public function findAllForNotifiable($notifiable_identifier, $notifiable_class) + { + return $this->createQueryBuilder('nn') + ->join('nn.notifiableEntity','ne') + ->where('ne.identifier = :identifier') + ->andWhere('ne.class = :class') + ->setParameter('identifier', $notifiable_identifier) + ->setParameter('class', $notifiable_class) + ->getQuery() + ->getResult() + ; + } + + /** + * @param $id + * + * @return \Doctrine\ORM\QueryBuilder + */ + public function findAllForNotifiableIdQb($id) + { + return $this->createQueryBuilder('nn') + ->join('nn.notification', 'n') + ->join('nn.notifiableEntity', 'ne') + ->where('ne.id = :id') + ->setParameter('id', $id) + ; + } + + /** + * Get the NotifiableNotifications for a NotifiableEntity id + * + * @param $id + * + * @return NotifiableNotification[] + */ + public function findAllForNotifiableId($id) + { + return $this->findAllForNotifiableIdQb($id)->getQuery()->getResult(); + } + + /** + * @param $notifiable_identifier + * @param $notifiable_class + * + * @return \Doctrine\ORM\QueryBuilder + */ + protected function getNotificationCoundQb($notifiable_identifier, $notifiable_class) + { + return $this->createQueryBuilder('nn') + ->select('COUNT(nn.id)') + ->join('nn.notifiableEntity', 'ne') + ->where('ne.identifier = :notifiable_identifier') + ->andWhere('ne.class = :notifiable_class') + ->setParameter('notifiable_identifier', $notifiable_identifier) + ->setParameter('notifiable_class', $notifiable_class) + ; + } + + /** + * Get the count of Notifications for a Notifiable entity. + * + * seen option results : + * null : get all notifications + * true : get seen notifications + * false : get unseen notifications + * + * @param string $notifiable_identifier + * @param string $notifiable_class + * @param bool|null $seen + * + * @return int + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\NoResultException + */ + public function getNotificationCount($notifiable_identifier, $notifiable_class, $seen = null) + { + $qb = $this->getNotificationCoundQb($notifiable_identifier,$notifiable_class); + + if ($seen !== null){ + $whereSeen = $seen ? 1 : 0; + $qb + ->andWhere('nn.seen = :seen') + ->setParameter('seen', $whereSeen); + } + return $qb->getQuery()->getSingleScalarResult(); + } + +} \ No newline at end of file diff --git a/Entity/Repository/NotifiableRepository.php b/Entity/Repository/NotifiableRepository.php new file mode 100644 index 0000000..a9e85c2 --- /dev/null +++ b/Entity/Repository/NotifiableRepository.php @@ -0,0 +1,60 @@ +createQueryBuilder('n')->select('e')->from($notifiableEntity->getClass(), 'e'); + + // map the identifier(s) to the value(s) + $identifiers = explode('-', $notifiableEntity->getIdentifier()); + foreach ($mapping as $key => $identifier) { + $qb->andWhere(sprintf('e.%s = :%s', $identifier, $identifier)); + $qb->setParameter($identifier, $identifiers[$key]); + } + + return $qb->getQuery()->getOneOrNullResult(); + } + + /** + * @param $id + * @param bool|null $seen + * + * @return NotifiableInterface[] + */ + public function findAllByNotification($id, $seen = null) + { + $qb = $this + ->createQueryBuilder('notifiable') + ->join('notifiable.notifiableNotifications', 'nn') + ->join('nn.notification', 'notification') + ->where('notification.id = :notification_id') + ->setParameter('notification_id', $id) + ; + + if ($seen !== null){ + $whereSeen = $seen ? 1 : 0; + $qb + ->andWhere('nn.seen = :seen') + ->setParameter('seen', $whereSeen) + ; + } + + return $qb->getQuery()->getResult(); + } +} \ No newline at end of file diff --git a/Entity/Repository/NotificationRepository.php b/Entity/Repository/NotificationRepository.php new file mode 100644 index 0000000..7e33bf6 --- /dev/null +++ b/Entity/Repository/NotificationRepository.php @@ -0,0 +1,48 @@ +findAllByNotifiableQb($identifier, $class); + + if ($seen !== null){ + $whereSeen = $seen ? 1 : 0; + $qb + ->andWhere('notifiable_notifications.seen = :seen') + ->setParameter('seen', $whereSeen) + ; + } + return $qb->getQuery()->getResult(); + } + + /** + * @param $identifier + * @param $class + * + * @return \Doctrine\ORM\QueryBuilder + */ + public function findAllByNotifiableQb($identifier, $class) + { + return $this->createQueryBuilder('notification') + ->addSelect('notifiable_notifications.seen') + ->join('notification.notifiableNotifications', 'notifiable_notifications') + ->join('notifiable_notifications.notifiableEntity', 'notifiable_entity') + ->where('notifiable_entity.identifier = :identifier') + ->andWhere('notifiable_entity.class = :class') + ->setParameter('identifier', $identifier) + ->setParameter('class', $class) + ; + } +} \ No newline at end of file diff --git a/Entity/UserNotificationInterface.php b/Entity/UserNotificationInterface.php deleted file mode 100755 index a1736cd..0000000 --- a/Entity/UserNotificationInterface.php +++ /dev/null @@ -1,40 +0,0 @@ -notification = $notification; + $this->notifiable = $notifiable; } /** - * @return AbstractNotification + * @return Notification */ public function getNotification() { return $this->notification; } + /** + * @return NotifiableInterface + */ + public function getNotifiable() + { + return $this->notifiable; + } + } diff --git a/Manager/NotificationManager.php b/Manager/NotificationManager.php index fedfb41..31fec2d 100755 --- a/Manager/NotificationManager.php +++ b/Manager/NotificationManager.php @@ -3,10 +3,14 @@ namespace Mgilet\NotificationBundle\Manager; use Doctrine\ORM\EntityManager; -use Mgilet\NotificationBundle\Entity\AbstractNotification; -use Mgilet\NotificationBundle\Entity\UserNotificationInterface; +use Doctrine\ORM\EntityNotFoundException; +use Mgilet\NotificationBundle\Entity\NotifiableEntity; +use Mgilet\NotificationBundle\Entity\NotifiableNotification; +use Mgilet\NotificationBundle\Entity\Notification; use Mgilet\NotificationBundle\Event\NotificationEvent; use Mgilet\NotificationBundle\MgiletNotificationEvents; +use Mgilet\NotificationBundle\NotifiableDiscovery; +use Mgilet\NotificationBundle\NotifiableInterface; use Symfony\Component\EventDispatcher\EventDispatcher; /** @@ -17,251 +21,620 @@ class NotificationManager { - private $om; - private $notification; - private $repository; - private $dispatcher; + protected $om; + protected $notifiableRepository; + protected $notificationRepository; + protected $notifiableNotificationRepository; + protected $dispatcher; + protected $discovery; /** * NotificationManager constructor. - * @param EntityManager $om - * @param $notification - * @internal param $class + * + * @param EntityManager $om + * @param NotifiableDiscovery $discovery + * */ - public function __construct(EntityManager $om, $notification) + public function __construct(EntityManager $om, NotifiableDiscovery $discovery) { $this->om = $om; - $this->notification = $notification; - $this->repository = $om->getRepository($notification); + $this->notifiableRepository = $om->getRepository('MgiletNotificationBundle:NotifiableEntity'); + $this->notificationRepository = $om->getRepository('MgiletNotificationBundle:Notification'); + $this->notifiableNotificationRepository = $this->om->getRepository('MgiletNotificationBundle:NotifiableNotification'); $this->dispatcher = new EventDispatcher(); + $this->discovery = $discovery; + + } + + /** + * Returns a list of available workers. + * + * @return array + * @throws \InvalidArgumentException + */ + public function getDiscoveryNotifiables() { + return $this->discovery->getNotifiables(); + } + + /** + * Returns one notifiable by name + * + * @param $name + * + * @return array + * + * @throws \InvalidArgumentException + * @throws \RuntimeException + */ + public function getNotifiable($name) { + $notifiables = $this->getDiscoveryNotifiables(); + if (isset($notifiables[$name])) { + return $notifiables[$name]; + } + + throw new \RuntimeException('Notifiable not found.'); + } + + /** + * Get the name of the notifiable + * + * @param NotifiableInterface $notifiable + * + * @return string|null + */ + public function getNotifiableName(NotifiableInterface $notifiable) + { + return $this->discovery->getNotifiableName($notifiable); + } + + /** + * @param NotifiableInterface $notifiable + * + * @return array + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function getNotifiableIdentifier(NotifiableInterface $notifiable) + { + $name = $this->getNotifiableName($notifiable); + + return $this->getNotifiable($name)['identifiers']; + } + + /** + * Get the identifier mapping for a NotifiableEntity + * + * @param NotifiableEntity $notifiableEntity + * + * @return array + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function getNotifiableEntityIdentifiers(NotifiableEntity $notifiableEntity) + { + $discoveryNotifiables = $this->getDiscoveryNotifiables(); + foreach ($discoveryNotifiables as $notifiable) { + if ($notifiable['class'] === $notifiableEntity->getClass()){ + return $notifiable['identifiers']; + } + } + throw new \RuntimeException('Unable to get the NotifiableEntity identifiers. This could be an Entity mapping issue'); + } + + /** + * Generates the identifier value to store a NotifiableEntity + * + * @param NotifiableInterface $notifiable + * + * @return string + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function generateIdentifier(NotifiableInterface $notifiable) + { + $Notifiableidentifiers = $this->getNotifiableIdentifier($notifiable); + $identifierValues = array(); + foreach ($Notifiableidentifiers as $identifier) { + $method = sprintf('get%s', ucfirst($identifier)); + $identifierValues[] = $notifiable->$method(); + } + + return implode('-', $identifierValues); + } + + /** + * Get a NotifiableEntity form a NotifiableInterface + * + * @param NotifiableInterface $notifiable + * + * @return NotifiableEntity + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\ORMInvalidArgumentException + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function getNotifiableEntity(NotifiableInterface $notifiable) + { + $identifier = $this->generateIdentifier($notifiable); + $class = get_class($notifiable); + $entity = $this->notifiableRepository->findOneBy(array( + 'identifier' => $identifier, + 'class' => $class + )); + + if (!$entity){ + $entity = new NotifiableEntity($identifier, $class); + $this->om->persist($entity); + $this->om->flush(); + } + + return $entity; + } + + /** + * @param NotifiableEntity $notifiableEntity + * + * @return NotifiableInterface + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function getNotifiableInterface(NotifiableEntity $notifiableEntity) + { + return $this->notifiableRepository->findNotifiableInterface( + $notifiableEntity, + $this->getNotifiableEntityIdentifiers($notifiableEntity) + ); + } + + /** + * @param $id + * + * @return NotifiableEntity|null + */ + public function getNotifiableEntityById($id) + { + return $this->notifiableRepository->findOneById($id); + } + + /** + * @param NotifiableInterface $notifiable + * @param Notification $notification + * + * @return NotifiableNotification|null + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\ORMInvalidArgumentException + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\NonUniqueResultException + */ + private function getNotifiableNotification(NotifiableInterface $notifiable, Notification $notification) + { + return $this->notifiableNotificationRepository->findOne( + $notification->getId(), + $this->getNotifiableEntity($notifiable) + ); + + } + + /** + * Avoid code duplication + * + * @param $flush + * + * @throws \Doctrine\ORM\OptimisticLockException + */ + private function flush($flush) + { + if ($flush){ + $this->om->flush(); + } + } + + /** + * Get all notifications + * + * @return Notification[] + */ + public function getAll() + { + return $this->notificationRepository->findAll(); + } + + /** + * @param NotifiableInterface $notifiable + * + * @throws \InvalidArgumentException + * @throws \RuntimeException + * + * @return array + */ + public function getNotifications(NotifiableInterface $notifiable) + { + return $this->notificationRepository->findAllByNotifiable($this->generateIdentifier($notifiable), get_class($notifiable)); } /** - * Get a notification by it's id + * @param NotifiableInterface $notifiable + * + * @return array + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function getUnseenNotifications(NotifiableInterface $notifiable) + { + return $this->notificationRepository->findAllByNotifiable($this->generateIdentifier($notifiable), get_class($notifiable), false); + } + + /** + * @param NotifiableInterface $notifiable + * + * @return array + * @throws \RuntimeException + * @throws \InvalidArgumentException + */ + public function getSeenNotifications(NotifiableInterface $notifiable) + { + return $this->notificationRepository->findAllByNotifiable($this->generateIdentifier($notifiable), get_class($notifiable), true); + } + + + /** + * Get one notification by id + * * @param $id - * @return AbstractNotification + * + * @return Notification */ - public function getNotificationById($id) + public function getNotification($id) { - return $this->repository->findOneBy(array('id' => $id)); + return $this->notificationRepository->findOneById($id); } /** - * Generate a notification - * @param $subject - * @param null $message - * @param null $link - * @return AbstractNotification + * @param string $subject + * @param string $message + * @param string $link + * + * @return Notification */ - public function generateNotification($subject, $message = null, $link = null) + public function createNotification($subject, $message = null, $link = null) { - /** @var AbstractNotification $notification */ - $notification = new $this->notification; + $notification = new Notification(); $notification ->setSubject($subject) ->setMessage($message) - ->setLink($link); - $this->om->persist($notification); + ->setLink($link) + ; $event = new NotificationEvent($notification); - $this->dispatcher->dispatch(MgiletNotificationEvents::onCreatedNotification, $event); + $this->dispatcher->dispatch(MgiletNotificationEvents::CREATED, $event); return $notification; } /** - * Add a notification to a user - * @param UserNotificationInterface $user - * @param AbstractNotification $notification - * @return AbstractNotification - * @throws \Doctrine\ORM\ORMInvalidArgumentException + * Add a Notification to a list of NotifiableInterface entities + * + * @param NotifiableInterface[] $notifiables + * @param Notification $notification + * @param bool $flush + * * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\ORMInvalidArgumentException + * @throws \InvalidArgumentException + * @throws \RuntimeException */ - public function addNotification(UserNotificationInterface $user, AbstractNotification $notification) + public function addNotification($notifiables, Notification $notification, $flush = false) { - $user->addNotification($notification); - $this->om->flush($notification); + foreach ($notifiables as $notifiable) { + $entity = $this->getNotifiableEntity($notifiable); - $event = new NotificationEvent($notification); - $this->dispatcher->dispatch(MgiletNotificationEvents::onNewNotification, $event); + $notifiableNotification = new NotifiableNotification(); + $entity->addNotifiableNotification($notifiableNotification); + $notification->addNotifiableNotification($notifiableNotification); - return $notification; + $event = new NotificationEvent($notification, $notifiable); + $this->dispatcher->dispatch(MgiletNotificationEvents::ASSIGNED, $event); + } + + $this->flush($flush); } /** - * @param UserNotificationInterface $user - * @param string $subject - * @param string|null $message - * @param string|null $link + * Deletes the link between a Notifiable and a Notification + * + * @param array $notifiables + * @param Notification $notification + * @param bool $flush + * * @throws \Doctrine\ORM\ORMInvalidArgumentException * @throws \Doctrine\ORM\OptimisticLockException + * @throws \InvalidArgumentException + * @throws \RuntimeException */ - public function createNotification(UserNotificationInterface $user, $subject, $message = null, $link = null) + public function removeNotification(array $notifiables, Notification $notification, $flush = false) { - $this->addNotification($user, $this->generateNotification($subject,$message,$link)); + $repo = $this->om->getRepository('MgiletNotificationBundle:NotifiableNotification'); + foreach ($notifiables as $notifiable) { + $repo->createQueryBuilder('nn') + ->delete() + ->where('nn.notifiableEntity = :entity') + ->andWhere('nn.notification = :notification') + ->setParameter('entity', $this->getNotifiableEntity($notifiable)) + ->setParameter('notification', $notification) + ->getQuery() + ->execute() + ; + + $event = new NotificationEvent($notification, $notifiable); + $this->dispatcher->dispatch(MgiletNotificationEvents::REMOVED, $event); + } + + $this->flush($flush); } /** - * Delete a notification - * @param AbstractNotification $notification + * @param Notification $notification + * + * @param bool $flush + * * @throws \Doctrine\ORM\ORMInvalidArgumentException * @throws \Doctrine\ORM\OptimisticLockException */ - public function removeNotification(AbstractNotification $notification) + public function deleteNotification(Notification $notification, $flush = false) { $this->om->remove($notification); - $this->om->flush(); + $this->flush($flush); $event = new NotificationEvent($notification); - $this->dispatcher->dispatch(MgiletNotificationEvents::onRemovedNotification, $event); + $this->dispatcher->dispatch(MgiletNotificationEvents::DELETED, $event); } /** - * Mark a notification as seen - * @param AbstractNotification $notification - * @return AbstractNotification - * @throws \Doctrine\ORM\ORMInvalidArgumentException + * @param NotifiableInterface $notifiable + * @param Notification $notification + * @param bool $flush + * + * @throws EntityNotFoundException * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\NonUniqueResultException */ - public function markAsSeen(AbstractNotification $notification) + public function markAsSeen(NotifiableInterface $notifiable, Notification $notification, $flush = false) { - $notification->setSeen(true); - $this->om->flush($notification); - - $event = new NotificationEvent($notification); - $this->dispatcher->dispatch(MgiletNotificationEvents::onSeenNotification, $event); + $nn = $this->getNotifiableNotification($notifiable,$notification); + if ($nn){ + $nn->setSeen(true); + $event = new NotificationEvent($notification, $notifiable); + $this->dispatcher->dispatch(MgiletNotificationEvents::SEEN, $event); + $this->flush($flush); + } else { + throw new EntityNotFoundException('The link between the notifiable and the notification has not been found'); + } + } - return $notification; + /** + * @param NotifiableInterface $notifiable + * @param Notification $notification + * @param bool $flush + * + * @throws EntityNotFoundException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\OptimisticLockException + */ + public function markAsUnseen(NotifiableInterface $notifiable, Notification $notification, $flush = false) + { + $nn = $this->getNotifiableNotification($notifiable,$notification); + if ($nn){ + $nn->setSeen(false); + $event = new NotificationEvent($notification, $notifiable); + $this->dispatcher->dispatch(MgiletNotificationEvents::UNSEEN, $event); + $this->flush($flush); + } else { + throw new EntityNotFoundException('The link between the notifiable and the notification has not been found'); + } } /** - * Mark all notifications as seen - * @param AbstractNotification[] $notifications - * @throws \Doctrine\ORM\ORMInvalidArgumentException + * @param NotifiableInterface $notifiable + * @param bool $flush + * + * @throws \InvalidArgumentException + * @throws \RuntimeException * @throws \Doctrine\ORM\OptimisticLockException */ - public function markAllAsSeen($notifications) + public function markAllAsSeen(NotifiableInterface $notifiable, $flush = false) { - foreach ($notifications as $notification) { - $this->markAsSeen($notification); + $nns = $this->notifiableNotificationRepository->findAllForNotifiable( + $this->generateIdentifier($notifiable), + get_class($notifiable) + ); + foreach ($nns as $nn) { + $nn->setSeen(true); + $event = new NotificationEvent($nn->getNotification(), $notifiable); + $this->dispatcher->dispatch(MgiletNotificationEvents::SEEN, $event); } + $this->flush($flush); } /** - * Get all notifications for a user - * @param UserNotificationInterface $user - * @return AbstractNotification[] list of notifications + * @param NotifiableInterface $notifiable + * @param Notification $notification + * + * @return bool + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws EntityNotFoundException */ - public function getUserNotifications($user) + public function isSeen(NotifiableInterface $notifiable, Notification $notification) { - return $this->repository->findBy(array('user' => $user),array('date' => 'DESC')); + $nn = $this->getNotifiableNotification($notifiable,$notification); + if ($nn){ + return $nn->isSeen(); + } + + throw new EntityNotFoundException('The link between the notifiable and the notification has not been found'); } /** - * Get all unseen notifications for a user - * @param UserNotificationInterface $user - * @return AbstractNotification[] + * @param NotifiableInterface $notifiable + * + * @return int + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\NoResultException */ - public function getUnseenUserNotifications($user) + public function getNotificationCount(NotifiableInterface $notifiable) { - return $this->repository->findBy( - array( - 'user' => $user, - 'seen' => false - ), - array('date' => 'DESC') + return $this->notifiableNotificationRepository->getNotificationCount( + $this->generateIdentifier($notifiable), + get_class($notifiable) ); } /** - * Get notification count for a user - * @param UserNotificationInterface $user + * @param NotifiableInterface $notifiable + * * @return int + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\NoResultException */ - public function getNotificationCount($user) + public function getUnseenNotificationCount(NotifiableInterface $notifiable) { - return count($this->repository->findBy(array('user' => $user),array('date' => 'DESC'))); + return $this->notifiableNotificationRepository->getNotificationCount( + $this->generateIdentifier($notifiable), + get_class($notifiable), + false + ); } /** - * Get unseen notification count for a user - * @param UserNotificationInterface $user + * @param NotifiableInterface $notifiable + * * @return int + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\NoResultException */ - public function getUnseenNotificationCount($user) + public function getSeenNotificationCount(NotifiableInterface $notifiable) { - return count($this->repository->findBy( - array( - 'user' => $user, - 'seen' => false - ), - array('date' => 'DESC') - )); + return $this->notifiableNotificationRepository->getNotificationCount( + $this->generateIdentifier($notifiable), + get_class($notifiable), + true + ); } /** - * @param AbstractNotification $notification - * @param \DateTime $date - * @return AbstractNotification + * @param Notification $notification + * @param \DateTime $dateTime + * @param bool $flush + * + * @return Notification + * @throws \Doctrine\ORM\OptimisticLockException */ - public function setNotificationDate(AbstractNotification $notification, \DateTime $date) + public function setDate(Notification $notification, \DateTime $dateTime, $flush = false) { - $notification->setDate($date); - $this->om->flush($notification); + $notification->setDate($dateTime); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::MODIFIED, $event); return $notification; } /** - * @param AbstractNotification $notification - * @param string $subject - * @return AbstractNotification + * @param Notification $notification + * @param string $subject + * @param bool $flush + * + * @return Notification + * @throws \Doctrine\ORM\OptimisticLockException */ - public function setNotificationSubject(AbstractNotification $notification, $subject) + public function setSubject(Notification $notification, $subject, $flush = false) { $notification->setSubject($subject); - $this->om->flush($notification); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::MODIFIED, $event); return $notification; } /** - * @param AbstractNotification $notification - * @param string $message - * @return AbstractNotification + * @param Notification $notification + * @param string $subject + * @param bool $flush + * + * @return Notification + * @throws \Doctrine\ORM\OptimisticLockException */ - public function setNotificationMessage(AbstractNotification $notification, $message) + public function setMessage(Notification $notification, $subject, $flush = false) { - $notification->setMessage($message); - $this->om->flush($notification); + $notification->setSubject($subject); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::MODIFIED, $event); return $notification; } /** - * @param AbstractNotification $notification - * @param string $link - * @return AbstractNotification + * @param Notification $notification + * @param string $link + * @param bool $flush + * + * @return Notification + * @throws \Doctrine\ORM\OptimisticLockException */ - public function setNotificationLink(AbstractNotification $notification, $link) + public function setLink(Notification $notification, $link, $flush = false) { $notification->setLink($link); - $this->om->flush($notification); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::MODIFIED, $event); return $notification; } /** - * @param AbstractNotification $notification - * @param boolean $seen - * @return AbstractNotification + * @param Notification $notification + * + * @return NotifiableInterface[] */ - public function setNotificationSeen(AbstractNotification $notification, $seen) + public function getNotifiables(Notification $notification) { - $notification->setLink($seen); - $this->om->flush($notification); + return $this->notifiableRepository->findAllByNotification($notification); + } - return $notification; + /** + * @param Notification $notification + * + * @return NotifiableInterface[] + */ + public function getUnseenNotifiables(Notification $notification) + { + return $this->notifiableRepository->findAllByNotification($notification, true); + } + + /** + * @param Notification $notification + * + * @return NotifiableInterface[] + */ + public function getSeenNotifiables(Notification $notification) + { + return $this->notifiableRepository->findAllByNotification($notification, false); } } diff --git a/MgiletNotificationEvents.php b/MgiletNotificationEvents.php index 78ed4ef..9f62915 100644 --- a/MgiletNotificationEvents.php +++ b/MgiletNotificationEvents.php @@ -5,36 +5,51 @@ final class MgiletNotificationEvents { /** - * The onCreatedNotification event occurs when an AbstractNotification is created. + * Occurs when a Notification is created. * - * This event allows you to access the notification. + * @Event("Mgilet\NotificationBundle\Event\NotificationEvent") + */ + const CREATED = 'mgilet.notification.created'; + + /** + * Occurs when a Notification is assigned to a NotifiableEntity. * * @Event("Mgilet\NotificationBundle\Event\NotificationEvent") */ - const onCreatedNotification = 'mgilet.notification.created_notification'; + const ASSIGNED = 'mgilet.notification.assigned'; /** - * The onNewNotification event occurs when an AbstractNotification is created assigned to an UserNotificationInterface. + * Occurs when a Notification is marked as seen. * - * This event allows you to access the notification. + * @Event("Mgilet\NotificationBundle\Event\NotificationEvent") + */ + const SEEN = 'mgilet.notification.seen'; + + /** + * Occurs when a Notification is marked as unseen. * * @Event("Mgilet\NotificationBundle\Event\NotificationEvent") */ - const onNewNotification = 'mgilet.notification.new_notification'; + const UNSEEN = 'mgilet.notification.unseen'; /** - * The onSeenNotification event occurs when an AbstractNotification is marked as seen. + * Occurs when a Notification is modified. * - * This event allows you to access the notification. + * @Event("Mgilet\NotificationBundle\Event\NotificationEvent") + */ + const MODIFIED = 'mgilet.notification.modified'; + + /** + * Occurs when a Notification is removed. * * @Event("Mgilet\NotificationBundle\Event\NotificationEvent") */ - const onSeenNotification = 'mgilet.notification.seen_notification'; + const REMOVED = 'mgilet.notification.removed'; /** - * The onRemovedNotification event occurs when an AbstractNotification is removed. + * Occurs when a Notification is deleted * * @Event("Mgilet\NotificationBundle\Event\NotificationEvent") */ - const onRemovedNotification = 'mgilet.notification.removed_notification'; + const DELETED = 'mgilet.notification.delete'; } diff --git a/NotifiableDiscovery.php b/NotifiableDiscovery.php new file mode 100644 index 0000000..40578ca --- /dev/null +++ b/NotifiableDiscovery.php @@ -0,0 +1,88 @@ +annotationReader = $annotationReader; + $this->em = $em; + $this->discoverNotifiables(); + } + + /** + * Returns all the workers + * @throws \InvalidArgumentException + */ + public function getNotifiables() + { + return $this->notifiables; + } + + /** + * @param NotifiableInterface $notifiable + * + * @return string|null + */ + public function getNotifiableName(NotifiableInterface $notifiable) + { + $annotation = $this->annotationReader->getClassAnnotation(new \ReflectionClass($notifiable), 'Mgilet\NotificationBundle\Annotation\Notifiable'); + if ($annotation){ + return $annotation->getName(); + } + + return null; + } + + /** + * Discovers workers + * @throws \InvalidArgumentException + */ + private function discoverNotifiables() + { + /** @var ClassMetadata[] $entities */ + $entities = $this->em->getMetadataFactory()->getAllMetadata(); + foreach ($entities as $entity) { + $class = $entity->name; + $annotation = $this->annotationReader->getClassAnnotation(new \ReflectionClass($class), 'Mgilet\NotificationBundle\Annotation\Notifiable'); + if ($annotation) { + $this->notifiables[$annotation->getName()] = [ + 'class' => $entity->name, + 'annotation' => $annotation, + 'identifiers' => $entity->getIdentifier() + ]; + } + } + } +} \ No newline at end of file diff --git a/NotifiableInterface.php b/NotifiableInterface.php new file mode 100644 index 0000000..5aaa2c7 --- /dev/null +++ b/NotifiableInterface.php @@ -0,0 +1,15 @@ + - - - - - - - - - - - - - - diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml new file mode 100644 index 0000000..c8c4605 --- /dev/null +++ b/Resources/config/routing.yml @@ -0,0 +1,3 @@ +notifications: + resource: "@MgiletNotificationBundle/Controller/" + type: annotation \ No newline at end of file diff --git a/Resources/config/services.yml b/Resources/config/services.yml index 151eb4e..8c60b22 100755 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -2,11 +2,16 @@ services: mgilet.notification: class: Mgilet\NotificationBundle\Manager\NotificationManager - arguments: ["@doctrine.orm.entity_manager", "%mgilet_notification.notification_class%"] + arguments: ["@doctrine.orm.entity_manager", '@mgilet.notifiable_discovery'] + + mgilet.notifiable_discovery: + class: Mgilet\NotificationBundle\NotifiableDiscovery + arguments: [ '@doctrine.orm.entity_manager', '@annotation_reader'] + mgilet.twig_extension: class: Mgilet\NotificationBundle\Twig\NotificationExtension public: false tags: - { name: twig.extension } - arguments: ['@mgilet.notification', '@security.token_storage', '@twig'] + arguments: ['@mgilet.notification', '@security.token_storage', '@twig', '@router'] diff --git a/Resources/doc/advanced-configuration.rst b/Resources/doc/advanced-configuration.rst deleted file mode 100755 index 15480e4..0000000 --- a/Resources/doc/advanced-configuration.rst +++ /dev/null @@ -1,67 +0,0 @@ -======================== -MgiletNotificationBundle -======================== ------------------------------------------------- -A simple Symfony 3 bundle for user notifications ------------------------------------------------- - -Advanced configuration -====================== - -By default this bundle is configured to use Implementations of ``UserNotificationInterface`` as user and ``AppBundle\Entity\Notification`` as notification entity. - -You can change this behavior by editing your ``config.yml`` file. - -Default bundle configuration: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Default configuration: - -.. code-block:: yaml - - # config.yml - - mgilet_notification: - notification_class: AppBundle\Entity\Notification - - -How to configure: -~~~~~~~~~~~~~~~~~ - -* ``notification_class``: the entity defining a notification (this class MUST extends the MappedSuperClass ``AbstractNotification`` ) - -Change the default configuration according to your existing class. - -Example : - -.. code-block:: yaml - - # config.yml - - mgilet_notification: - notification_class: AcmeBundle\Entity\MyNotification - - -Go further : ------------- - -`go further`_ - ----------------------------------------------- - -* `installation`_ - -* `basic usage`_ - -* `overriding parts of the bundle`_ - -* `advanced configuration`_ - -* `go further`_ - - -.. _installation: index.rst -.. _basic usage: usage.rst -.. _overriding parts of the bundle: overriding.rst -.. _advanced configuration: advanced-configuration.rst -.. _go further: further.rst diff --git a/Resources/doc/further.rst b/Resources/doc/further.rst index c2770d1..810aca0 100755 --- a/Resources/doc/further.rst +++ b/Resources/doc/further.rst @@ -32,17 +32,11 @@ And thank you for using this. * `basic usage`_ -* `overriding parts of the bundle`_ - -* `advanced configuration`_ - * `go further`_ .. _installation: index.rst .. _basic usage: usage.rst -.. _overriding parts of the bundle: overriding.rst -.. _advanced configuration: advanced-configuration.rst .. _go further: further.rst .. _Github: https://github.com/maximilienGilet/notification-bundle \ No newline at end of file diff --git a/Resources/doc/index.rst b/Resources/doc/index.rst index e3a6da2..c2ecb2a 100755 --- a/Resources/doc/index.rst +++ b/Resources/doc/index.rst @@ -12,25 +12,10 @@ Installation Prerequisites ------------- -This version of the bundle requires Symfony 2.7+. For better rendering Bootstrap 3 is recommended. +This version of the bundle requires Symfony 2.7+. Warning : For now only Doctrine ORM is supported -Translations -~~~~~~~~~~~~ - -If you wish to use default texts provided in this bundle, you have to make -sure you have translator enabled in your config. - -.. code-block:: yaml - - # app/config/config.yml - - framework: - translator: ~ - -For more information about translations, check `Symfony documentation`_. - Basic installation: ------------------- @@ -59,19 +44,17 @@ Then add the following line in the AppKernel.php: ); } -Define User and Notification classes: -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Configure notifiables classes: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The goal of this bundle is to provide a ``Notification`` to a ``User``, so you need to define these classes. +The goal of this bundle is to make one or many entities ``notifiables``. -The bundle provides base classes which are already mapped for most fields -to make it easier to create your entity. Here is how you use it: +1. Use the ``@Notifiable`` annotation on your entity +2. Implement the ``Mgilet\NotificationBundle\NotifiableInterface`` interface (it's an empty interface) -1. Implement ``UserNotificationInterface`` interface (from the ``Model`` folder ) on your ``User`` entity -2. Map the ``notifications`` field (we will create it just after) -3. Implement ``UserNotificationInterface`` methods in your ``User`` class +And that's it ! -Sample configuration: +Example: .. code-block:: php @@ -87,141 +70,16 @@ Sample configuration: /** * @ORM\Entity - * @ORM\Table(name="user") + * @ORM\Table(name="my_entity") + * @Notifiable(name="my_entity") */ - class User implements UserNotificationInterface + class MyEntity implements Mgilet\NotificationBundle\NotifiableInterface { ... - // link to notifications - /** - * @var Notification - * @ORM\OneToMany(targetEntity="AppBundle\Entity\Notification", mappedBy="user", orphanRemoval=true) - */ - protected $notifications; - - ... - - public function __construct() - { - ... - $this->notifications = new ArrayCollection(); - } - - ... - - // method implementation for UserNotificationInterface - - /** - * {@inheritdoc} - */ - public function getNotifications() - { - return $this->notifications; - } - - /** - * {@inheritdoc} - */ - public function addNotification($notification) - { - if (!$this->notifications->contains($notification)) { - $this->notifications[] = $notification; - $notification->setUser($this); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function removeNotification($notification) - { - if ($this->notifications->contains($notification)) { - $this->notifications->removeElement($notification); - } - - return $this; - } - - /** - * {@inheritdoc} - */ - public function getIdentifier() - { - $this->getId(); - } - - } - -Now we need the Notification class. - -Simply extend the provided MappedSuperClass ``AbstractNotification`` class (from the ``Entity`` folder) and link it to the ``User`` entity. - -Here is a sample configuration: - -.. code-block:: php - - id; - } - - /** - * @return User - */ - public function getUser() - { - return $this->user; - } - - /** - * @param User $user - * @return Notification - */ - public function setUser($user) - { - $this->user = $user; - $user->addNotification($this); - - return $this; - } - - } +You can set as many entities ``notifiables`` as you want. +Entities with multiple identifiers are supported Update Doctrine ~~~~~~~~~~~~~~~ @@ -259,38 +117,20 @@ In order to enable the controller, simply put this in your ``routing.yml`` : prefix: /notifications -Assets : -~~~~~~~~ - -By installing this bundle with composer, all assets will be copied. if it doesn't work, execute the following command: - -**Symfony 2.x** - -.. code-block:: bash - - $ php app/console assets:install +Translations (optionnal) +~~~~~~~~~~~~~~~~~~~~~~~~ -**Symfony 3.x** - -.. code-block:: bash - - $ php bin/console assets:install - - -Class not located in AppBundle : -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your ``Notification`` entity is not located in ``AppBundle`` or have different name than default, you must define it's path in your ``config`` file. - -Example of configuration : +If you wish to use default texts provided in this bundle, you have to make +sure you have translator enabled in your config. .. code-block:: yaml - # config.yml + # app/config/config.yml - mgilet_notification: - notification_class: AnotherBundle\Entity\MyNotification # default value is AppBundle\Entity\Notification + framework: + translator: ~ +For more information about translations, check `Symfony documentation`_. Basic usage : ~~~~~~~~~~~~~ @@ -303,17 +143,11 @@ Go to `basic usage`_ * `basic usage`_ -* `overriding parts of the bundle`_ - -* `advanced configuration`_ - * `go further`_ .. _installation: index.rst .. _basic usage: usage.rst -.. _overriding parts of the bundle: overriding.rst -.. _advanced configuration: advanced-configuration.rst .. _go further: further.rst .. _Symfony documentation: https://symfony.com/doc/current/book/translation.html diff --git a/Resources/doc/overriding.rst b/Resources/doc/overriding.rst deleted file mode 100755 index 7b98504..0000000 --- a/Resources/doc/overriding.rst +++ /dev/null @@ -1,66 +0,0 @@ -======================== -MgiletNotificationBundle -======================== ------------------------------------------------- -A simple Symfony 3 bundle for user notifications ------------------------------------------------- - -Overriding parts of the bundle -============================== - -While providing configured templates, you can customize them to adjust it to your needs. - -The following will show you how. - -For more information about overriding parts of bundles, see the `Symfony documentation`_. - - -Twig templates: ---------------- - -In order to override templates provided by this bundle, you need to setup folders like this:: - - app/Resources - ├─ MgiletNotificationBundle/ - │ ├─ views/ - │ │ ├─ notification_dropdown.html.twig - │ │ └─ notification_list.html.twig - │ │ └─ notifications.html.twig - - -* ``notification_dropdown.html.twig`` is the template called when you use the dropdown option of ``mgilet_notification_render`` Twig method. - -* ``notification_list.html.twig`` is the template called when you use the list option of ``mgilet_notification_render`` Twig method. - -* ``notifications.html.twig`` is the template called by the ``NotificationController`` when responding to the ``/notifications`` route call. - -Rest of the bundle: -------------------- - -According to the `Symfony documentation`_ you can override any other part of this bundle. - -Advanced configuration : ------------------------- - -Go to `advanced configuration`_. - ----------------------------------------------- - -* `installation`_ - -* `basic usage`_ - -* `overriding parts of the bundle`_ - -* `advanced configuration`_ - -* `go further`_ - - -.. _installation: index.rst -.. _basic usage: usage.rst -.. _overriding parts of the bundle: overriding.rst -.. _advanced configuration: advanced-configuration.rst -.. _go further: further.rst - -.. _Symfony documentation: http://symfony.com/doc/current/cookbook/bundles/override.html \ No newline at end of file diff --git a/Resources/doc/usage.rst b/Resources/doc/usage.rst index a432d5e..36a74c4 100755 --- a/Resources/doc/usage.rst +++ b/Resources/doc/usage.rst @@ -8,7 +8,7 @@ A simple Symfony 3 bundle for user notifications Usage ===== -This bundle provides some methods and helpers in order to be as simple as possible to use. +This bundle provides manuy methods and helpers in order to be as simple as possible to use. mgilet.notification service --------------------------- @@ -18,10 +18,10 @@ This is the notification manager. It's designed to do manage notification in a e Currently, it allows you to: * create notifications -* add notifications to users -* mark notifications as "seen" -* get notifications for a user -* get notification count +* add notifications to entities +* mark notifications as "seen"/"unseen" +* get notifications for an entity +* get notification counts * ... and much more ! Basic usage @@ -31,6 +31,8 @@ Lets write a minimalistic route : ``send-notification``. This sample route will send a notification to the user going there. +Your ``User`` entity will need to be ``notifiable`` (see `installation`_) + Sample route: .. code-block:: php @@ -54,13 +56,15 @@ Sample route: public function sendNotification(Request $request) { $manager = $this->get('mgilet.notification'); - $notif = $manager->generateNotification('Hello world !'); + // or the one-line method : + // $manager->createNotification('Notification subject','Some random text','http://google.fr'); + $notif = $manager->createNotification('Hello world !'); $notif->setMessage('This a notification.'); $notif->setLink('http://symfony.com/'); - $manager->addNotification($this->getUser(), $notif); - // or the one-line method : - // $manager->createNotification($this->getUser(), 'Notification subject','Some random text','http://google.fr'); + // you can add a notification to a list of entities + // the third parameter ``$flush`` allows you to directly flush the entities + $manager->addNotification(array($this->getUser()), $notif); return $this->redirectToRoute('homepage'); } @@ -75,10 +79,13 @@ By using the ``NotificationManager`` you can listen to events thrown by the mana List of events: -* ``onCreatedNotification`` When a notification is created -* ``onNewNotification`` When a notification is assigned to a user -* ``onSeenNotification`` When a notification is marked as seen -* ``onRemovedNotification`` When a notification is removed +* ``'mgilet.notification.created'`` +* ``'mgilet.notification.assigned'`` -> added to a user +* ``'mgilet.notification.seen'`` +* ``'mgilet.notification.unseen'`` +* ``'mgilet.notification.modified'`` +* ``'mgilet.notification.removed'`` +* ``'mgilet.notification.delete'`` Twig functions @@ -86,17 +93,21 @@ Twig functions This bundle also provides some useful twig functions helping you to design a great user experience. -Notice : Bootstrap 3 is highly recommended for best results. - -If you want to make your own twig template, see : `overriding parts of the bundle`_ +If you want to make your own twig template, see : `Symfony documentation`_ List of functions : ~~~~~~~~~~~~~~~~~~~ * mgilet_notification_count -* mgilet_unseen_notification_count +* mgilet_notification_unseen_count + +These functions will display the current notification count for a given notifiable + +:: + + {{ mgilet_notification_count() }} {# all notifications #} -These functions will display the current notification count for the current user. + {{ mgilet_unseen_notification_count }} {# unseen notifications #} ------------------ @@ -106,56 +117,30 @@ This function will render notifications for a user (current by default). It take Currently, 2 options are available : -* display - * list : will display a simple list of all notifications - * dropdown : a responsive Bootstrap dropdown with full notification handling - -note : one argument is required - * seen * true : will display all notification (default behavior) * false : will display only unseen notifications -As optional second argument, you can pass a user. By default current user is selected +* template + * use the the twig file you provide instead of the default one. NOTE : the notification list is called ``notificationList`` -Usage: -~~~~~~ -**Notification count :** :: - {{ mgilet_notification_count() }} {# all notifications #} - - {{ mgilet_unseen_notification_count }} {# unseen notifications #} + {{ mgilet_notification_render(notifiableEntity) }} -**Rendering:** + // only unseen notifications + {{ mgilet_notification_render(notifiableEntity ,{'seen': false }) }} -Dropdown with all notifications:: + // custom template + {{ mgilet_notification_render({ 'template': 'Path/to/my/template.html.twig'}) }} - {{ mgilet_notification_render({ 'display': 'dropdown', 'seen': true }) }} - -Or:: - - {{ mgilet_notification_render({ 'display': 'dropdown' }) }} - - -Only unseen notifications in dropdown:: - - {{ mgilet_notification_render({ 'display': 'dropdown', 'seen': false }) }} - -List with all notifications:: - - {{ mgilet_notification_render({ 'display': 'list', 'seen': true }) }} - - -Or:: - - {{ mgilet_notification_render({ 'display': 'list' }) }} {# does the same thing #} +------------------ +* mgilet_notification_generate_path -List with only unseen notifications:: +this function will help you using the bundle's controller. It will generate links to the provided routes (list, mark_as_seen, mark_as_unseen, mark_all_as_seen) - {{ mgilet_notification_render({ 'display': 'list', 'seen': false }) }} Notification controller: @@ -167,18 +152,11 @@ The controller is located in ``vendor/mgilet/notification-bundle/Controller/NotificationController``. -Built in routes : -~~~~~~~~~~~~~~~~~ - -* ``/notifications`` : return the ``list`` template with all notifications -* ``/notifications/{notification}/markAsSeen`` : mark the given notification as seen -* ``/notifications/{notification}/markAsUnseen``: mark the given notification as unseen -* ``/notifications/markAllAsSeen`` : mark all notifications as seen for the user -Overriding parts of the bundle : --------------------------------- +Go further : +------------ -Go to `overriding parts of the bundle`_ +Go to `go further`_ ---------------------------------------------- @@ -186,15 +164,11 @@ Go to `overriding parts of the bundle`_ * `basic usage`_ -* `overriding parts of the bundle`_ - -* `advanced configuration`_ - * `go further`_ .. _installation: index.rst .. _basic usage: usage.rst -.. _overriding parts of the bundle: overriding.rst -.. _advanced configuration: advanced-configuration.rst .. _go further: further.rst + +.. _Symfony documentation: http://symfony.com/doc/current/bundles/override.html diff --git a/Resources/public/css/mgilet_notification.css b/Resources/public/css/mgilet_notification.css deleted file mode 100755 index 9b26477..0000000 --- a/Resources/public/css/mgilet_notification.css +++ /dev/null @@ -1,54 +0,0 @@ -.notification-menu { - width: 40vw; - max-width: 430px; - position: absolute; - top: 100%; - right: 0; - z-index: 1000; -} - -.scrollable-menu { - height: auto; - max-height: 50vh; - overflow-x: hidden; - margin: 0; -} - -.scrollable-menu a { - text-decoration: none; -} - -.scrollable-menu li { - padding: 6px 0 6px 10px; -} - -.scrollable-menu li:hover { - background-color: #eee; -} - -.scrollable-menu a.text-muted:hover { - color: #777; -} - -.no-padding { - padding: 0 !important; -} - -.no-margin { - margin: 0 !important; -} - -.no-float { - float: none; -} - -.list-group .list-group-item .row-content .action-secondary { - position: absolute; - right: 16px; - top: 8px; -} - -.seen *{ - color: #777; - font-weight:normal; -} diff --git a/Resources/public/js/ajax-notification.js b/Resources/public/js/ajax-notification.js deleted file mode 100755 index 3517565..0000000 --- a/Resources/public/js/ajax-notification.js +++ /dev/null @@ -1,69 +0,0 @@ -(function() { - - // ajax request to mark a notification as seen - function markAsSeen(e) { - var xhttp = new XMLHttpRequest(); - var element = e.target; - xhttp.onreadystatechange = function () { - // on success - if (xhttp.readyState == 4 && xhttp.status == 200) { - // mark notification as seen - element.parentNode.classList+= ' seen'; - // remove button - element.remove(); - // decrease notification count - var notificationCounter = document.getElementById('notificationCount'); - var notificationNumber = parseInt(notificationCounter.innerHTML); - notificationNumber--; - notificationCounter.innerHTML = notificationNumber.toString(); - if (notificationNumber == 0){ - notificationCounter.parentNode.parentNode.classList = ''; - } - } - }; - xhttp.open("POST", element, true); - xhttp.send(); - } - - function markAllAsSeen(e) { - var xhttp = new XMLHttpRequest(); - var element = e.target; - xhttp.onreadystatechange = function () { - // on success - if (xhttp.readyState == 4 && xhttp.status == 200) { - // add "seen" class for all notifications - var notifications = document.getElementsByClassName('notification'); - for (var notification of notifications){ - notification.children[0].classList += ' seen'; - } - // remove action buttons - var paras = document.getElementsByClassName('ajax-notification'); - while(paras[0]) { - paras[0].parentNode.removeChild(paras[0]); - } - // set notification count to 0 - var notificationCount = document.getElementById('notificationCount'); - notificationCount.innerHTML = '0'; - notificationCount.parentNode.parentNode.classList = ''; - } - }; - xhttp.open("POST", element, true); - xhttp.send(); - } - - // mark as seen button handler - var btns = document.getElementsByClassName('ajax-notification'); - for(var btn of btns){ - btn.addEventListener('click', function (e) { - e.preventDefault(); - markAsSeen(e); - }); - } - - // mark all as seen button handler - document.getElementById('notification-MarkAllAsSeen').addEventListener('click',function (e) { - e.preventDefault(); - markAllAsSeen(e); - }); - -})(); diff --git a/Resources/views/notification_dropdown.html.twig b/Resources/views/notification_dropdown.html.twig deleted file mode 100755 index 06ccddf..0000000 --- a/Resources/views/notification_dropdown.html.twig +++ /dev/null @@ -1,47 +0,0 @@ -{% if mgilet_unseen_notification_count() > 0 %} -
  • - {% else %} -
  • - {% endif %} - {{ mgilet_unseen_notification_count() }} -
    - -
    -
  • - - diff --git a/Resources/views/notification_list.html.twig b/Resources/views/notification_list.html.twig index 3d4addf..efdd7ba 100755 --- a/Resources/views/notification_list.html.twig +++ b/Resources/views/notification_list.html.twig @@ -1,4 +1,4 @@ Notifications : -{% for notification in notifications %} - {{ notification }} +{% for notificationItem in notificationList %} + {{ notificationItem[0] }} {% endfor %} diff --git a/Resources/views/notifications.html.twig b/Resources/views/notifications.html.twig index 3d4addf..8f99ac3 100755 --- a/Resources/views/notifications.html.twig +++ b/Resources/views/notifications.html.twig @@ -1,4 +1,22 @@ Notifications : -{% for notification in notifications %} - {{ notification }} -{% endfor %} +
    + +
    +
      + {% for notifiableNotification in notifiableNotifications %} +
    • + {{ notifiableNotification.notification }} +
      + seen : {{ notifiableNotification.seen }} +
      + +
      +
      + +
      +
    • + {% endfor %} +
    diff --git a/Twig/NotificationExtension.php b/Twig/NotificationExtension.php index bf26332..a4bf497 100755 --- a/Twig/NotificationExtension.php +++ b/Twig/NotificationExtension.php @@ -2,7 +2,12 @@ namespace Mgilet\NotificationBundle\Twig; +use Doctrine\DBAL\Exception\InvalidArgumentException; +use Mgilet\NotificationBundle\Entity\NotifiableEntity; +use Mgilet\NotificationBundle\Entity\Notification; use Mgilet\NotificationBundle\Manager\NotificationManager; +use Mgilet\NotificationBundle\NotifiableInterface; +use Symfony\Component\Routing\Router; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Twig_Extension; @@ -14,6 +19,7 @@ class NotificationExtension extends Twig_Extension protected $notificationManager; protected $storage; protected $twig; + protected $router; /** * NotificationExtension constructor. @@ -21,11 +27,12 @@ class NotificationExtension extends Twig_Extension * @param TokenStorage $storage * @param \Twig_Environment $twig */ - public function __construct(NotificationManager $notificationManager, TokenStorage $storage, \Twig_Environment $twig) + public function __construct(NotificationManager $notificationManager, TokenStorage $storage, \Twig_Environment $twig, Router $router) { $this->notificationManager = $notificationManager; $this->storage = $storage; $this->twig = $twig; + $this->router = $router; } /** @@ -40,7 +47,10 @@ public function getFunctions() new \Twig_SimpleFunction('mgilet_notification_count', array($this, 'countNotifications'), array( 'is_safe' => array('html') )), - new \Twig_SimpleFunction('mgilet_unseen_notification_count', array($this, 'countUnseenNotifications'), array( + new \Twig_SimpleFunction('mgilet_notification_unseen_count', array($this, 'countUnseenNotifications'), array( + 'is_safe' => array('html') + )), + new \Twig_SimpleFunction('mgilet_notification_generate_path', array($this, 'generatePath'), array( 'is_safe' => array('html') )) ); @@ -48,101 +58,148 @@ public function getFunctions() /** * Rendering notifications in Twig - * @param array $options - * @param null $user + * + * @param array $options + * @param NotifiableInterface $notifiable + * * @return null|string + * @throws \RuntimeException + * @throws \InvalidArgumentException */ - public function render($options = array(), $user = null) + public function render(NotifiableInterface $notifiable, array $options = array()) { if( !array_key_exists('seen',$options)) { $options['seen'] = true; } - if ($options['display'] === 'list') { - return $this->renderNotifications($user, $options['seen']); - } - if ($options['display'] === 'dropdown') { - return $this->renderDropdownNotifications($user, $options['seen']); - } - return null; + + return $this->renderNotifications($notifiable, $options); } /** - * Render notifications for a user - * @param null $user - * @param bool $seen + * Render notifications of the notifiable as a list + * + * @param NotifiableInterface $notifiable + * @param array $options + * * @return string - * @internal param \Twig_Environment $twig + * + * @throws \RuntimeException + * @throws \InvalidArgumentException */ - public function renderNotifications($user = null, $seen = true) + public function renderNotifications(NotifiableInterface $notifiable, array $options) { - $user = $this->getUser($user); - if ($seen) { - $notifications = $this->notificationManager->getUserNotifications($user); + if ($options['seen']) { + $notifications = $this->notificationManager->getNotifications($notifiable); } else { - $notifications = $this->notificationManager->getUnseenUserNotifications($user); + $notifications = $this->notificationManager->getUnseenNotifications($notifiable); } - return $this->twig->render('MgiletNotificationBundle::notification_list.html.twig', + // if the template option is set, use custom template + $template = array_key_exists('template', $options) ? $options['template'] : 'MgiletNotificationBundle::notification_list.html.twig'; + + return $this->twig->render($template, array( - 'notifications' => $notifications + 'notificationList' => $notifications ) ); } /** - * Render notifications for a user in a dropdown (Bootstrap 3 highly recommended) - * @param null $user - * @param bool $seen - * @return mixed - */ - public function renderDropdownNotifications($user = null, $seen = true) - { - $user = $this->getUser($user); - if ($seen) { - $notifications = $this->notificationManager->getUserNotifications($user); - } else { - $notifications = $this->notificationManager->getUnseenUserNotifications($user); - } - - return $this->twig->render('MgiletNotificationBundle::notification_dropdown.html.twig', array( - 'notifications' => $notifications - )); - } - - /** - * Display the total count of notifications for this user - * @param null $user + * Display the total count of notifications for the notifiable + * + * @param NotifiableInterface $notifiable + * * @return int + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\NoResultException */ - public function countNotifications($user = null) + public function countNotifications(NotifiableInterface $notifiable) { - $user = $this->getUser($user); - return $this->notificationManager->getNotificationCount($user); + return $this->notificationManager->getNotificationCount($notifiable); } /** - * Display the count of unseen notifications for this user - * @param null $user + * Display the count of unseen notifications for this notifiable + * + * @param NotifiableInterface $notifiable + * * @return int + * + * @throws \RuntimeException + * @throws \InvalidArgumentException + * @throws \Doctrine\ORM\NonUniqueResultException + * @throws \Doctrine\ORM\NoResultException */ - public function countUnseenNotifications($user = null) + public function countUnseenNotifications(NotifiableInterface $notifiable) { - $user = $this->getUser($user); - return $this->notificationManager->getUnseenNotificationCount($user); + return $this->notificationManager->getUnseenNotificationCount($notifiable); } /** - * If no user is specified return current user - * @param null $user - * @return mixed user + * Returns the path to the NotificationController action + * + * @param $route + * @param $notifiable + * @param Notification|null $notification + * + * @return \InvalidArgumentException|string + * @throws \RuntimeException + * @throws \Doctrine\DBAL\Exception\InvalidArgumentException + * @throws \Doctrine\ORM\OptimisticLockException + * @throws \Doctrine\ORM\ORMInvalidArgumentException + * @throws \InvalidArgumentException */ - private function getUser($user = null) + public function generatePath($route, $notifiable, Notification $notification = null) { - if (!$user) { - return $this->storage->getToken()->getUser(); + if ($notifiable instanceof NotifiableInterface){ + $notifiableId = $this->notificationManager->getNotifiableEntity($notifiable)->getId(); + } elseif ($notifiable instanceof NotifiableEntity){ + $notifiableId = $notifiable->getId(); + } else { + throw new InvalidArgumentException('You must provide a NotifiableInterface or NotifiableEntity object'); } - return $user; + switch ($route){ + case 'notification_list': + return $this->router->generate( + 'notification_list', + array('notifiable' => $notifiableId) + ); + break; + case 'notification_mark_as_seen': + if (!$notification){ + throw new \InvalidArgumentException('You must provide a Notification Entity'); + } + + return $this->router->generate( + 'notification_mark_as_seen', + array( + 'notifiable' => $notifiableId, + 'notification' => $notification->getId() + ) + ); + break; + case 'notification_mark_as_unseen': + if (!$notification){ + throw new \InvalidArgumentException('You must provide a Notification Entity'); + } + + return $this->router->generate( + 'notification_mark_as_unseen', + array( + 'notifiable' => $notifiableId, + 'notification' => $notification->getId() + ) + ); + break; + case 'notification_mark_all_as_seen': + return $this->router->generate('notification_mark_all_as_seen', array('notifiable' => $notifiableId)); + break; + default: + return new \InvalidArgumentException('You must provide a valid route path. Paths availables : notification_list, notification_mark_as_seen, notification_mark_as_unseen, notification_mark_all_as_seen'); + } } /** diff --git a/composer.json b/composer.json index 6dd3a5b..d36b9e3 100755 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ }], "extra" : { "branch-alias" : { - "dev-master" : "1.0-dev" + "dev-master" : "2.0-dev" } } } From 8eb88525f4c58c2ada2b1fe28338179f7238f106 Mon Sep 17 00:00:00 2001 From: Maximilien Gilet Date: Tue, 17 Oct 2017 09:07:50 +0200 Subject: [PATCH 5/6] Update documentation --- README.md | 59 ++++++++++++++++++++++------------------- Resources/doc/usage.rst | 4 +-- 2 files changed, 33 insertions(+), 30 deletions(-) mode change 100755 => 100644 README.md diff --git a/README.md b/README.md old mode 100755 new mode 100644 index 542bd01..7b0b48b --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@

    mgilet/notification-bundle

    -A simple Symfony bundle to notify user +An easy yet powerful notification bundle for Symfony

    Latest Stable Version @@ -16,22 +16,21 @@ A simple Symfony bundle to notify user

    mgilet/notificationBundle

    -Create and manage user notifications in an efficient way. +Create and manage notifications in an efficient way. Symfony support : * 2.7.x * 2.8.x * 3.x - -Bootstrap > 3.x highly recommended ## Features -- Easy notification management -- Simple Twig render method -- Pretty Twig template (dropdown using Bootstrap 3) -- Fully customizable - Easy setup +- Easy to use +- Powerful notification management +- Simple Twig render methods +- Fully customizable +- Multiple notifiables entities - No bloated dependencies (little requirements) Notice: Only Doctrine ORM is supported for now. @@ -50,40 +49,44 @@ First : $ composer require mgilet/notification-bundle ``` -See [documentation](Resources/doc/index.rst) for next steps + + +**See [documentation](Resources/doc/index.rst) for next steps** + + ### Basic usage ```php -class DefaultController extends Controller +class MyController extends Controller { ... - /** - * @Route("/send-notification", name="send_notification") - * @param Request $request - * @return \Symfony\Component\HttpFoundation\RedirectResponse - */ public function sendNotification(Request $request) { - $manager = $this->get('mgilet.notification'); - $notif = $manager->generateNotification('Hello world !'); - $notif - ->setMessage('This a notification.') - ->setLink('http://symfony.com/'); - $manager->addNotification($this->getUser(), $notif); - - // or the one-line method : - // $manager->createNotification($this->getUser(), 'Notification subject','Some random text','http://google.fr'); - - return $this->redirectToRoute('homepage'); + $manager = $this->get('mgilet.notification'); + $notif = $manager->createNotification('Hello world !'); + $notif->setMessage('This a notification.'); + $notif->setLink('http://symfony.com/'); + // or the one-line method : + // $manager->createNotification('Notification subject','Some random text','http://google.fr'); + + // you can add a notification to a list of entities + // the third parameter `$flush` allows you to directly flush the entities + $manager->addNotification(array($this->getUser()), $notif, true); + + ... } ``` -See [HERE](Resources/doc/usage.rst) for more -## Translations + +**See [HERE](Resources/doc/usage.rst) for more** + + + +## ****Translations For now this bundle is only translated to de, en, es, fr, it. diff --git a/Resources/doc/usage.rst b/Resources/doc/usage.rst index 36a74c4..3e7bbe6 100755 --- a/Resources/doc/usage.rst +++ b/Resources/doc/usage.rst @@ -56,11 +56,11 @@ Sample route: public function sendNotification(Request $request) { $manager = $this->get('mgilet.notification'); - // or the one-line method : - // $manager->createNotification('Notification subject','Some random text','http://google.fr'); $notif = $manager->createNotification('Hello world !'); $notif->setMessage('This a notification.'); $notif->setLink('http://symfony.com/'); + // or the one-line method : + // $manager->createNotification('Notification subject','Some random text','http://google.fr'); // you can add a notification to a list of entities // the third parameter ``$flush`` allows you to directly flush the entities From 14e05612682f360203387fb76d8d0f4e14b49483 Mon Sep 17 00:00:00 2001 From: Maximilien Gilet Date: Tue, 17 Oct 2017 09:10:58 +0200 Subject: [PATCH 6/6] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b0b48b..8d695f7 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ class MyController extends Controller -## ****Translations +## Translations For now this bundle is only translated to de, en, es, fr, it.