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 65c0548..d2467ca 100755 --- a/Controller/NotificationController.php +++ b/Controller/NotificationController.php @@ -2,13 +2,12 @@ namespace Mgilet\NotificationBundle\Controller; -use Mgilet\NotificationBundle\Model\AbstractNotification; -use Mgilet\NotificationBundle\Model\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 c53f939..a7d76d2 100755 --- a/DependencyInjection/MgiletNotificationExtension.php +++ b/DependencyInjection/MgiletNotificationExtension.php @@ -20,14 +20,12 @@ class MgiletNotificationExtension extends Extension */ 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/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/Model/AbstractNotification.php b/Entity/Notification.php similarity index 55% rename from Model/AbstractNotification.php rename to Entity/Notification.php index b81518d..526372a 100755 --- a/Model/AbstractNotification.php +++ b/Entity/Notification.php @@ -1,62 +1,68 @@ seen = false; $this->date = new \DateTime(); + $this->notifiableNotifications = new ArrayCollection(); } /** @@ -143,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/Event/NotificationEvent.php b/Event/NotificationEvent.php new file mode 100644 index 0000000..ee7ee85 --- /dev/null +++ b/Event/NotificationEvent.php @@ -0,0 +1,43 @@ +notification = $notification; + $this->notifiable = $notifiable; + } + + /** + * @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 0132e5c..31fec2d 100755 --- a/Manager/NotificationManager.php +++ b/Manager/NotificationManager.php @@ -3,8 +3,15 @@ namespace Mgilet\NotificationBundle\Manager; use Doctrine\ORM\EntityManager; -use Mgilet\NotificationBundle\Model\AbstractNotification; -use Mgilet\NotificationBundle\Model\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; /** * Class NotificationManager @@ -14,174 +21,620 @@ class NotificationManager { - private $om; - private $notification; - private $repository; + 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)); + } + + /** + * @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); } /** - * Get a notification by it's id + * @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); + ->setLink($link) + ; + + $event = new NotificationEvent($notification); + $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->persist($user); - $this->om->flush(); + foreach ($notifiables as $notifiable) { + $entity = $this->getNotifiableEntity($notifiable); - return $notification; + $notifiableNotification = new NotifiableNotification(); + $entity->addNotifiableNotification($notifiableNotification); + $notification->addNotifiableNotification($notifiableNotification); + + $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->persist($notification); - $this->om->flush(); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $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->persist($notification); - $this->om->flush(); + $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 Notification $notification + * @param \DateTime $dateTime + * @param bool $flush + * + * @return Notification + * @throws \Doctrine\ORM\OptimisticLockException + */ + public function setDate(Notification $notification, \DateTime $dateTime, $flush = false) + { + $notification->setDate($dateTime); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::MODIFIED, $event); + + return $notification; + } + + /** + * @param Notification $notification + * @param string $subject + * @param bool $flush + * + * @return Notification + * @throws \Doctrine\ORM\OptimisticLockException + */ + public function setSubject(Notification $notification, $subject, $flush = false) + { + $notification->setSubject($subject); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::MODIFIED, $event); + + return $notification; + } + + /** + * @param Notification $notification + * @param string $subject + * @param bool $flush + * + * @return Notification + * @throws \Doctrine\ORM\OptimisticLockException + */ + public function setMessage(Notification $notification, $subject, $flush = false) + { + $notification->setSubject($subject); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::MODIFIED, $event); + + return $notification; + } + + /** + * @param Notification $notification + * @param string $link + * @param bool $flush + * + * @return Notification + * @throws \Doctrine\ORM\OptimisticLockException + */ + public function setLink(Notification $notification, $link, $flush = false) + { + $notification->setLink($link); + $this->flush($flush); + + $event = new NotificationEvent($notification); + $this->dispatcher->dispatch(MgiletNotificationEvents::MODIFIED, $event); + + return $notification; + } + + /** + * @param Notification $notification + * + * @return NotifiableInterface[] + */ + public function getNotifiables(Notification $notification) + { + return $this->notifiableRepository->findAllByNotification($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/MgiletNotificationBundle.php b/MgiletNotificationBundle.php index bd27d28..0258a95 100755 --- a/MgiletNotificationBundle.php +++ b/MgiletNotificationBundle.php @@ -15,5 +15,5 @@ */ class MgiletNotificationBundle extends Bundle { - + } diff --git a/MgiletNotificationEvents.php b/MgiletNotificationEvents.php new file mode 100644 index 0000000..9f62915 --- /dev/null +++ b/MgiletNotificationEvents.php @@ -0,0 +1,55 @@ +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 @@ +mgilet/notification-bundle
-A simple Symfony bundle to notify user
+An easy yet powerful notification bundle for Symfony
@@ -16,22 +16,21 @@ A simple Symfony bundle to notify user