From 6384527720a86123eea3fa315ee4b0f28798baaf Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 12:38:43 +0300 Subject: [PATCH 01/33] Some refactoring. --- .../Controller/MainController.php | 34 ++----------------- .../PointToolsBundle/Entity/Subscription.php | 2 +- .../Entity/SubscriptionEvent.php | 2 +- .../Entity/SubscriptionEventRepository.php | 25 ++++++++++++++ .../Entity/SubscriptionRepository.php | 24 +++++++++++++ .../Bundle/PointToolsBundle/Entity/User.php | 2 +- .../Entity/UserRepository.php | 18 ++++++++++ 7 files changed, 73 insertions(+), 34 deletions(-) create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php index 876a6d4..b821e17 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php @@ -13,38 +13,10 @@ class MainController extends Controller /** @var EntityManager $em */ $em = $this->getDoctrine()->getManager(); - /** @var QueryBuilder $qb */ - $qb = $em->getRepository('SkobkinPointToolsBundle:User')->createQueryBuilder('u'); - - // All users in the system count - $usersCount = $qb->select('COUNT(u)')->getQuery()->getSingleScalarResult(); - - $qb = $em->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s'); - - // Service subscribers count - $subscribersCount = $qb - ->select('COUNT(s)') - ->innerJoin('s.author', 'a') - ->where('a.login = :login') - ->setParameter('login', $this->container->getParameter('point_login')) - ->getQuery()->getSingleScalarResult() - ; - - $qb = $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->createQueryBuilder('se'); - - $now = new \DateTime(); - - $eventsCount = $qb - ->select('COUNT(se)') - ->where('se.date > :time') - ->setParameter('time', $now->sub(new \DateInterval('PT24H'))) - ->getQuery()->getSingleScalarResult() - ; - return $this->render('SkobkinPointToolsBundle:Main:index.html.twig', [ - 'users_count' => $usersCount, - 'subscribers_count' => $subscribersCount, - 'events_count' => $eventsCount, + 'users_count' => $em->getRepository('SkobkinPointToolsBundle:User')->getUsersCount(), + 'subscribers_count' => $em->getRepository('SkobkinPointToolsBundle:Subscription')->getUserSubscribersCountById($this->container->getParameter('point_id')), + 'events_count' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastDayEventsCount(), 'service_login' => $this->container->getParameter('point_login'), ]); } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php index 03b18a3..588c5e4 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php @@ -10,7 +10,7 @@ use Doctrine\ORM\Mapping as ORM; * @ORM\Table(name="subscriptions.subscriptions", uniqueConstraints={ * @ORM\UniqueConstraint(name="subscription_unique", columns={"author_id", "subscriber_id"})} * ) - * @ORM\Entity + * @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Entity\SubscriptionRepository") */ class Subscription { diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php index 2b98686..1c1c4a2 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php @@ -12,7 +12,7 @@ use Doctrine\ORM\Mapping as ORM; * @ORM\Index(name="subscriber_idx", columns={"subscriber_id"}), * @ORM\Index(name="date_idx", columns={"date"}) * }) - * @ORM\Entity + * @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Entity\SubscriptionEventRepository") * @ORM\HasLifecycleCallbacks */ class SubscriptionEvent diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php new file mode 100644 index 0000000..c22c588 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php @@ -0,0 +1,25 @@ +createQueryBuilder('se'); + + $now = new \DateTime(); + + $eventsCount = $qb + ->select('COUNT(se)') + ->where('se.date > :time') + ->setParameter('time', $now->sub(new \DateInterval('PT24H'))) + ->getQuery()->getSingleScalarResult() + ; + } +} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php new file mode 100644 index 0000000..5684c03 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php @@ -0,0 +1,24 @@ +createQueryBuilder('s'); + return $qb + ->select('COUNT(s)') + ->innerJoin('s.author', 'a') + ->where('a.id = :id') + ->setParameter('id', $id) + ->getQuery()->getSingleScalarResult() + ; + } +} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php index 051f9be..35ee886 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php @@ -9,7 +9,7 @@ use Doctrine\ORM\Mapping as ORM; * User * * @ORM\Table(name="users.users") - * @ORM\Entity + * @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Entity\UserRepository") * @ORM\HasLifecycleCallbacks */ class User diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php new file mode 100644 index 0000000..f5a736a --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php @@ -0,0 +1,18 @@ +createQueryBuilder('u'); + + return $qb->select('COUNT(u)')->getQuery()->getSingleScalarResult(); + } +} \ No newline at end of file From 0f930376249123565b95e2a3f1a0ec0af6b82027 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 12:39:30 +0300 Subject: [PATCH 02/33] 500 ms timeout. --- .../PointToolsBundle/Command/UpdateSubscriptionsCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Command/UpdateSubscriptionsCommand.php b/src/Skobkin/Bundle/PointToolsBundle/Command/UpdateSubscriptionsCommand.php index 76c02ee..9b1b8d9 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Command/UpdateSubscriptionsCommand.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Command/UpdateSubscriptionsCommand.php @@ -128,7 +128,7 @@ class UpdateSubscriptionsCommand extends ContainerAwareCommand } // @todo move to the config - usleep(200000); + usleep(500000); } } } \ No newline at end of file From da7b8894f5d538b7f40e38d86a5c965175ab4842 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 12:42:59 +0300 Subject: [PATCH 03/33] Repository fix. --- .../PointToolsBundle/Entity/SubscriptionEventRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php index c22c588..47b6f27 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php @@ -15,7 +15,7 @@ class SubscriptionEventRepository extends EntityRepository $now = new \DateTime(); - $eventsCount = $qb + return $qb ->select('COUNT(se)') ->where('se.date > :time') ->setParameter('time', $now->sub(new \DateInterval('PT24H'))) From c87192a1f28c201886e1c6c00aa199ab9732c69e Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 13:34:53 +0300 Subject: [PATCH 04/33] More refactoring. --- .../Controller/UserController.php | 59 +++---------------- .../Entity/SubscriptionEventRepository.php | 23 ++++++++ .../Entity/SubscriptionRepository.php | 21 +++++++ .../Entity/UserRepository.php | 40 +++++++++++++ 4 files changed, 91 insertions(+), 52 deletions(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php index d294de5..22d2e12 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php @@ -2,9 +2,7 @@ namespace Skobkin\Bundle\PointToolsBundle\Controller; -use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\EntityManager; -use Skobkin\Bundle\PointToolsBundle\Entity\TopUserDTO; use Skobkin\Bundle\PointToolsBundle\Entity\User; use Skobkin\Bundle\PointToolsBundle\Service\UserApi; use Symfony\Bundle\FrameworkBundle\Controller\Controller; @@ -17,16 +15,11 @@ class UserController extends Controller */ public function showAction($login) { - /** @var QueryBuilder $qb */ - $qb = $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:User')->createQueryBuilder('u'); + /** @var EntityManager $em */ + $em = $this->getDoctrine()->getManager(); - $user = $qb - ->select('u') - ->where('LOWER(u.login) = LOWER(:login)') - ->setMaxResults(1) - ->setParameter('login', $login) - ->getQuery()->getOneOrNullResult() - ; + /** @var User $user */ + $user = $em->getRepository('SkobkinPointToolsBundle:User')->findUserByLogin($login); if (!$user) { throw $this->createNotFoundException('User ' . $login . ' not found.'); @@ -34,56 +27,18 @@ class UserController extends Controller $userApi = $this->container->get('skobkin_point_tools.api_user'); - $qb = $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:User')->createQueryBuilder('u'); - - $subscribers = $qb - ->select('u') - ->innerJoin('u.subscriptions', 's') - ->where('s.author = :author') - ->orderBy('u.login', 'asc') - ->setParameter('author', $user->getId()) - ->getQuery()->getResult() - ; - - $qb = $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->createQueryBuilder('se'); - - $subscriptionsEvents = $qb - ->select() - ->where('se.author = :author') - ->orderBy('se.date', 'desc') - ->setMaxResults(10) - ->setParameter('author', $user) - ->getQuery()->getResult() - ; - return $this->render('SkobkinPointToolsBundle:User:show.html.twig', [ 'user' => $user, - 'subscribers' => $subscribers, - 'log' => $subscriptionsEvents, + 'subscribers' => $em->getRepository('SkobkinPointToolsBundle:User')->findUserSubscribersById($user->getId()), + 'log' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getUserLastSubscriptionEventsById($user, 10), 'avatar_url' => $userApi->getAvatarUrl($user, UserApi::AVATAR_SIZE_LARGE), ]); } public function topAction() { - /** @var EntityManager $em */ - $em = $this->getDoctrine()->getManager(); - - /** @var QueryBuilder $qb */ - $qb = $em->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s'); - - /** @var TopUserDTO[] $topUsers */ - $topUsers = $qb - ->select(['COUNT(s.subscriber) as cnt', 'NEW SkobkinPointToolsBundle:TopUserDTO(a.login, COUNT(s.subscriber))']) - ->innerJoin('s.author', 'a') - ->orderBy('cnt', 'desc') - ->groupBy('a.id') - ->setMaxResults(30) - ->getQuery()->getResult() - ; - return $this->render('@SkobkinPointTools/User/top.html.twig', [ - 'top_users' => $topUsers + 'top_users' => $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:Subscription')->getTopUsers(), ]); } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php index 47b6f27..8937a90 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php @@ -22,4 +22,27 @@ class SubscriptionEventRepository extends EntityRepository ->getQuery()->getSingleScalarResult() ; } + + /** + * @param User $user + * @param integer $limit + * @return SubscriptionEvent[] + */ + public function getUserLastSubscriptionEventsById(User $user, $limit) + { + if (!is_int($limit)) { + throw new \InvalidArgumentException('$limit must be an integer'); + } + + $qb = $this->createQueryBuilder('se'); + + return $qb + ->select() + ->where('se.author = :author') + ->orderBy('se.date', 'desc') + ->setMaxResults($limit) + ->setParameter('author', $user) + ->getQuery()->getResult() + ; + } } \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php index 5684c03..1dcd55f 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php @@ -12,6 +12,10 @@ class SubscriptionRepository extends EntityRepository */ public function getUserSubscribersCountById($id) { + if (!is_int($id)) { + throw new \InvalidArgumentException('$id must be an integer'); + } + $qb = $this->createQueryBuilder('s'); return $qb ->select('COUNT(s)') @@ -21,4 +25,21 @@ class SubscriptionRepository extends EntityRepository ->getQuery()->getSingleScalarResult() ; } + + /** + * @return TopUserDTO[] + */ + public function getTopUsers() + { + $qb = $this->createQueryBuilder('s'); + + return $qb + ->select(['COUNT(s.subscriber) as cnt', 'NEW SkobkinPointToolsBundle:TopUserDTO(a.login, COUNT(s.subscriber))']) + ->innerJoin('s.author', 'a') + ->orderBy('cnt', 'desc') + ->groupBy('a.id') + ->setMaxResults(30) + ->getQuery()->getResult() + ; + } } \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php index f5a736a..ed2dd2b 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php @@ -6,6 +6,24 @@ use Doctrine\ORM\EntityRepository; class UserRepository extends EntityRepository { + /** + * @param string $login + * @return User[] + * @throws \Doctrine\ORM\NonUniqueResultException + */ + public function findUserByLogin($login) + { + $qb = $this->createQueryBuilder('u'); + + return $qb + ->select('u') + ->where('LOWER(u.login) = LOWER(:login)') + ->setMaxResults(1) + ->setParameter('login', $login) + ->getQuery()->getOneOrNullResult() + ; + } + /** * @return integer */ @@ -15,4 +33,26 @@ class UserRepository extends EntityRepository return $qb->select('COUNT(u)')->getQuery()->getSingleScalarResult(); } + + /** + * @param integer $id + * @return User[] + */ + public function findUserSubscribersById($id) + { + if (!is_int($id)) { + throw new \InvalidArgumentException('$id must be an integer'); + } + + $qb = $this->createQueryBuilder('u'); + + return $qb + ->select('u') + ->innerJoin('u.subscriptions', 's') + ->where('s.author = :author') + ->orderBy('u.login', 'asc') + ->setParameter('author', $id) + ->getQuery()->getResult() + ; + } } \ No newline at end of file From 41c7e575245f02be8664a1b4ef1d658591c49888 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 13:37:45 +0300 Subject: [PATCH 05/33] Small fix. --- .../Bundle/PointToolsBundle/Controller/MainController.php | 1 - src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php index b821e17..4d411fb 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php @@ -20,5 +20,4 @@ class MainController extends Controller 'service_login' => $this->container->getParameter('point_login'), ]); } - } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php index ed2dd2b..92f047e 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php @@ -7,6 +7,8 @@ use Doctrine\ORM\EntityRepository; class UserRepository extends EntityRepository { /** + * Case-insensitive user search + * * @param string $login * @return User[] * @throws \Doctrine\ORM\NonUniqueResultException From ac297dc0a2c56fca76737590763900de68729af3 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 13:41:29 +0300 Subject: [PATCH 06/33] Moar refactoring. --- .../Controller/UserController.php | 2 +- .../Entity/SubscriptionRepository.php | 17 --------------- .../Entity/UserRepository.php | 21 +++++++++++++++++++ 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php index 22d2e12..8f442e3 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php @@ -38,7 +38,7 @@ class UserController extends Controller public function topAction() { return $this->render('@SkobkinPointTools/User/top.html.twig', [ - 'top_users' => $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:Subscription')->getTopUsers(), + 'top_users' => $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:User')->getTopUsers(), ]); } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php index 1dcd55f..3bc7064 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionRepository.php @@ -25,21 +25,4 @@ class SubscriptionRepository extends EntityRepository ->getQuery()->getSingleScalarResult() ; } - - /** - * @return TopUserDTO[] - */ - public function getTopUsers() - { - $qb = $this->createQueryBuilder('s'); - - return $qb - ->select(['COUNT(s.subscriber) as cnt', 'NEW SkobkinPointToolsBundle:TopUserDTO(a.login, COUNT(s.subscriber))']) - ->innerJoin('s.author', 'a') - ->orderBy('cnt', 'desc') - ->groupBy('a.id') - ->setMaxResults(30) - ->getQuery()->getResult() - ; - } } \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php index 92f047e..e8092eb 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php @@ -57,4 +57,25 @@ class UserRepository extends EntityRepository ->getQuery()->getResult() ; } + + /** + * @return TopUserDTO[] + */ + public function getTopUsers($limit = 30) + { + if (!is_int($limit)) { + throw new \InvalidArgumentException('$limit must be an integer'); + } + + $qb = $this->createQueryBuilder('s'); + + return $qb + ->select(['COUNT(s.subscriber) as cnt', 'NEW SkobkinPointToolsBundle:TopUserDTO(a.login, COUNT(s.subscriber))']) + ->innerJoin('s.author', 'a') + ->orderBy('cnt', 'desc') + ->groupBy('a.id') + ->setMaxResults($limit) + ->getQuery()->getResult() + ; + } } \ No newline at end of file From aedc15a2041e13979588dbeeeca8277682cf85a9 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 14:03:46 +0300 Subject: [PATCH 07/33] Last events on the main page. --- .../Controller/MainController.php | 1 + .../Entity/SubscriptionEventRepository.php | 26 ++++++++++ .../Resources/translations/messages.ru.yml | 3 ++ .../Resources/views/Main/index.html.twig | 52 +++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php index 4d411fb..f6a3d34 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/MainController.php @@ -18,6 +18,7 @@ class MainController extends Controller 'subscribers_count' => $em->getRepository('SkobkinPointToolsBundle:Subscription')->getUserSubscribersCountById($this->container->getParameter('point_id')), 'events_count' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastDayEventsCount(), 'service_login' => $this->container->getParameter('point_login'), + 'last_events' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getLastSubscriptionEvents(10), ]); } } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php index 8937a90..f17f01c 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php @@ -3,6 +3,7 @@ namespace Skobkin\Bundle\PointToolsBundle\Entity; use Doctrine\ORM\EntityRepository; +use Doctrine\ORM\Mapping\ClassMetadata; class SubscriptionEventRepository extends EntityRepository { @@ -45,4 +46,29 @@ class SubscriptionEventRepository extends EntityRepository ->getQuery()->getResult() ; } + + /** + * Get last $limit subscriptions + * + * @param integer $limit + * @return SubscriptionEvent[] + */ + public function getLastSubscriptionEvents($limit) + { + if (!is_int($limit)) { + throw new \InvalidArgumentException('$limit must be an integer'); + } + + $qb = $this->createQueryBuilder('se'); + + return $qb + ->select() + ->orderBy('se.date', 'desc') + ->setMaxResults($limit) + ->getQuery() + ->setFetchMode('SkobkinPointToolsBundle:SubscriptionEvent', 'author', ClassMetadata::FETCH_EAGER) + ->setFetchMode('SkobkinPointToolsBundle:SubscriptionEvent', 'subscriber', ClassMetadata::FETCH_EAGER) + ->getResult() + ; + } } \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml index ab55c66..a46ea28 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml @@ -13,6 +13,9 @@ Source code: Исходный код All users: Всего пользователей Subscribed users: Подписчиков сервиса 24 hours events: Событий за сутки +Author: Автор +Subscriber: Подписчик +Last events: Последние события Username: Имя пользователя Search: Поиск diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig index 2a2c3c9..7bfe4cc 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig @@ -15,6 +15,58 @@ + {# TODO classes #} +
+ {% if last_events|length > 0 %} +
+
+ +
+
+ + + + + + + + + + + {% for event in last_events %} + + + + + + + {% endfor %} + +
{{ 'Subscriber'|trans }}{{ 'Author'|trans }}{{ 'Action'|trans }}{{ 'Date'|trans }}
+ @{{ event.subscriber.login }} + + @{{ event.author.login }} + + + + {# Use DateTime helper: https://sonata-project.org/bundles/intl/master/doc/reference/datetime.html #} + {{ event.date|date('d F Y H:i:s') }} +
+
+
+
+
+ {% else %} + + {% endif %} +
+
{{ 'All users'|trans }}
From 5778404813f71476a5285a226378eb53a0745b7f Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 14:19:57 +0300 Subject: [PATCH 08/33] Query crutchy fix. --- src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php index e8092eb..3c9db8d 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/UserRepository.php @@ -67,7 +67,8 @@ class UserRepository extends EntityRepository throw new \InvalidArgumentException('$limit must be an integer'); } - $qb = $this->createQueryBuilder('s'); + // TODO: refactor query + $qb = $this->getEntityManager()->getRepository('SkobkinPointToolsBundle:Subscription')->createQueryBuilder('s'); return $qb ->select(['COUNT(s.subscriber) as cnt', 'NEW SkobkinPointToolsBundle:TopUserDTO(a.login, COUNT(s.subscriber))']) From 27754e913404a3a85e05bb280f14e467daea5583 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Tue, 23 Jun 2015 14:20:21 +0300 Subject: [PATCH 09/33] Separate last events page. --- app/Resources/views/base.html.twig | 1 + .../Controller/EventsController.php | 20 +++++++ .../Resources/config/routing.yml | 6 +- .../Resources/translations/messages.ru.yml | 1 + .../Resources/views/Events/last.html.twig | 55 +++++++++++++++++++ .../Resources/views/Main/index.html.twig | 52 ------------------ 6 files changed, 82 insertions(+), 53 deletions(-) create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Controller/EventsController.php create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Resources/views/Events/last.html.twig diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig index 9afff0b..a5731f9 100644 --- a/app/Resources/views/base.html.twig +++ b/app/Resources/views/base.html.twig @@ -26,6 +26,7 @@
- {# TODO classes #} -
- {% if last_events|length > 0 %} -
-
- -
-
- - - - - - - - - - - {% for event in last_events %} - - - - - - - {% endfor %} - -
{{ 'Subscriber'|trans }}{{ 'Author'|trans }}{{ 'Action'|trans }}{{ 'Date'|trans }}
- @{{ event.subscriber.login }} - - @{{ event.author.login }} - - - - {# Use DateTime helper: https://sonata-project.org/bundles/intl/master/doc/reference/datetime.html #} - {{ event.date|date('d F Y H:i:s') }} -
-
-
-
-
- {% else %} - - {% endif %} -
-
{{ 'All users'|trans }}
From 57cedf5417cb0e44bae4fa9dd14d38a1ab687984 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Fri, 26 Jun 2015 15:57:51 +0300 Subject: [PATCH 10/33] =?UTF-8?q?=D0=9A=D0=BE=D0=BB=D0=B8=D1=87=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D0=BE=20=D0=BF=D0=BE=D0=B4=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D1=87=D0=B8=D0=BA=D0=BE=D0=B2=20=D0=BD=D0=B0=20=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D0=B5=20=D0=BF=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D1=8F.=20=D0=9D?= =?UTF-8?q?=D0=BE=D0=BC=D0=B5=D1=80=D0=B0=20=D0=B2=20=D1=82=D0=BE=D0=BF?= =?UTF-8?q?=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Bundle/PointToolsBundle/Resources/views/User/show.html.twig | 2 +- .../Bundle/PointToolsBundle/Resources/views/User/top.html.twig | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig index 1c06370..75f5842 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig @@ -15,7 +15,7 @@ diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig index 8d9903c..6c11d28 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig @@ -8,6 +8,7 @@ + @@ -15,6 +16,7 @@ {% for user in top_users %} + From 6f2cca5799305f94f2c9f96d099f4282513bf8d5 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Fri, 26 Jun 2015 18:05:26 +0300 Subject: [PATCH 11/33] =?UTF-8?q?=D0=A2=D0=BE=D0=BF=20=D0=B2=20=D0=B2?= =?UTF-8?q?=D0=B8=D0=B4=D0=B5=20=D0=B3=D1=80=D0=B0=D1=84=D0=B8=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/AppKernel.php | 1 + composer.json | 3 +- composer.lock | 172 +++++++++++++++++- .../Controller/UserController.php | 52 +++++- .../Resources/translations/messages.ru.yml | 3 +- .../Resources/views/User/top.html.twig | 34 ++-- 6 files changed, 237 insertions(+), 28 deletions(-) diff --git a/app/AppKernel.php b/app/AppKernel.php index 49fa758..694ca22 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -18,6 +18,7 @@ class AppKernel extends Kernel new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(), new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(), new Misd\GuzzleBundle\MisdGuzzleBundle(), + new Ob\HighchartsBundle\ObHighchartsBundle(), new Skobkin\Bundle\PointToolsBundle\SkobkinPointToolsBundle(), ); diff --git a/composer.json b/composer.json index 7c6a1af..058368b 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "incenteev/composer-parameter-handler": "~2.0", "misd/guzzle-bundle": "~1.0", "doctrine/migrations": "1.0.*@dev", - "doctrine/doctrine-migrations-bundle": "2.1.*@dev" + "doctrine/doctrine-migrations-bundle": "2.1.*@dev", + "ob/highcharts-bundle": "^1.2" }, "require-dev": { "sensio/generator-bundle": "~2.3" diff --git a/composer.lock b/composer.lock index e29b519..1c65f54 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "6f8a78a0b471fceb335f2e83e43985ef", + "hash": "bfe197a5b9e16d562aba89cb97f380bf", "packages": [ { "name": "doctrine/annotations", @@ -519,7 +519,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/1e8cd4415bd2f893eb828216b529a75e8b61d579", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/861d3564c03b3867845ffd87d9b19f49dc673c69", "reference": "1e8cd4415bd2f893eb828216b529a75e8b61d579", "shasum": "" }, @@ -698,7 +698,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/abb87d84ed21fd30c27bd3b52252a495a36d32fb", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/43619782df4e9ae6be6b24557f0429471eeee848", "reference": "abb87d84ed21fd30c27bd3b52252a495a36d32fb", "shasum": "" }, @@ -1242,6 +1242,61 @@ ], "time": "2015-03-09 09:58:04" }, + { + "name": "ob/highcharts-bundle", + "version": "1.2", + "target-dir": "Ob/HighchartsBundle", + "source": { + "type": "git", + "url": "https://github.com/marcaube/ObHighchartsBundle.git", + "reference": "cce67aa209f2a8b14db520c8e50a15cd5f5c23d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/marcaube/ObHighchartsBundle/zipball/cce67aa209f2a8b14db520c8e50a15cd5f5c23d0", + "reference": "cce67aa209f2a8b14db520c8e50a15cd5f5c23d0", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/symfony": "~2.3", + "zendframework/zend-json": "2.3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Ob\\HighchartsBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marc Aubé" + } + ], + "description": "Symfony2 Bundle that ease the use of highcharts to display rich graph and charts in your app", + "homepage": "https://github.com/marcaube/ObHighchartsBundle", + "keywords": [ + "Symfony2", + "chart", + "charting", + "charts", + "graph", + "graphs", + "highcharts", + "marcaube", + "ob" + ], + "time": "2014-08-04 23:56:54" + }, { "name": "psr/log", "version": "1.0.0", @@ -1683,7 +1738,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/symfony/zipball/7493c2bef54fb818c5304bdd9d2194890b839422", + "url": "https://api.github.com/repos/symfony/symfony/zipball/a0af4ef1bfe8788288a2e5f95635134cba3ea842", "reference": "7493c2bef54fb818c5304bdd9d2194890b839422", "shasum": "" }, @@ -1903,6 +1958,115 @@ "templating" ], "time": "2015-04-19 08:30:27" + }, + { + "name": "zendframework/zend-json", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-json.git", + "reference": "cf8e594a8a6516d06c25a3dc07e3be462fbea84d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-json/zipball/cf8e594a8a6516d06c25a3dc07e3be462fbea84d", + "reference": "cf8e594a8a6516d06c25a3dc07e3be462fbea84d", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-http": "self.version", + "zendframework/zend-server": "self.version" + }, + "suggest": { + "zendframework/zend-http": "Zend\\Http component", + "zendframework/zend-server": "Zend\\Server component", + "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev", + "dev-develop": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Json\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://github.com/zendframework/zend-json", + "keywords": [ + "json", + "zf2" + ], + "time": "2014-03-12 16:10:15" + }, + { + "name": "zendframework/zend-stdlib", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-stdlib.git", + "reference": "426b5396e89e7da2db9678bc9a0b57865f84fe0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/426b5396e89e7da2db9678bc9a0b57865f84fe0f", + "reference": "426b5396e89e7da2db9678bc9a0b57865f84fe0f", + "shasum": "" + }, + "require": { + "php": ">=5.3.23" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-serializer": "self.version", + "zendframework/zend-servicemanager": "self.version" + }, + "suggest": { + "zendframework/zend-eventmanager": "To support aggregate hydrator usage", + "zendframework/zend-filter": "To support naming strategy hydrator usage", + "zendframework/zend-serializer": "Zend\\Serializer component", + "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev", + "dev-develop": "2.3-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-stdlib", + "keywords": [ + "stdlib", + "zf2" + ], + "time": "2014-03-12 16:10:15" } ], "packages-dev": [ diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php index 8f442e3..42c160b 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php @@ -3,9 +3,11 @@ namespace Skobkin\Bundle\PointToolsBundle\Controller; use Doctrine\ORM\EntityManager; +use Skobkin\Bundle\PointToolsBundle\Entity\TopUserDTO; use Skobkin\Bundle\PointToolsBundle\Entity\User; use Skobkin\Bundle\PointToolsBundle\Service\UserApi; use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Ob\HighchartsBundle\Highcharts\Highchart; use Symfony\Component\HttpFoundation\Request; class UserController extends Controller @@ -37,8 +39,13 @@ class UserController extends Controller public function topAction() { + $topUsers = $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:User')->getTopUsers(); + + $topChart = $this->createTopUsersGraph($topUsers); + return $this->render('@SkobkinPointTools/User/top.html.twig', [ - 'top_users' => $this->getDoctrine()->getManager()->getRepository('SkobkinPointToolsBundle:User')->getTopUsers(), + 'top_users' => $topUsers, + 'top_chart' => $topChart, ]); } @@ -54,4 +61,47 @@ class UserController extends Controller } return $this->redirectToRoute('user_show', ['login' => $login]); } + + /** + * @param TopUserDTO[] $topUsers + * @return Highchart + */ + private function createTopUsersGraph(array $topUsers = []) + { + $translator = $this->container->get('translator'); + + $chartData = [ + 'titles' => [], + 'subscribers' => [], + ]; + + // Preparing chart data + foreach ($topUsers as $user) { + $chartData['titles'][] = $user->login; + $chartData['subscribers'][] = $user->subscribersCount; + } + + // Chart + $series = [[ + 'name' => $translator->trans('Subscribers'), + 'data' => $chartData['subscribers'], + ]]; + + // Initializing chart + $ob = new Highchart(); + $ob->chart->renderTo('top-chart'); + $ob->chart->type('bar'); + $ob->title->text($translator->trans('Top users')); + $ob->xAxis->title(['text' => null]); + $ob->xAxis->categories($chartData['titles']); + $ob->yAxis->title(['text' => $translator->trans('amount')]); + $ob->plotOptions->bar([ + 'dataLabels' => [ + 'enabled' => true + ] + ]); + $ob->series($series); + + return $ob; + } } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml index 4b57053..e1e9d66 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/translations/messages.ru.yml @@ -33,4 +33,5 @@ No log data found: Лог отсутствует # Топ пользователей Top users: Популярные пользователи -Subscribers count: Подписчиков \ No newline at end of file +Subscribers count: Подписчиков +amount: Количество \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig index 6c11d28..d5ba95d 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/top.html.twig @@ -2,25 +2,17 @@ {% block header_title %}Top @ Point Tools{% endblock %} -{% block content %} -

{{ 'Top users'|trans }}

- -
# {{ 'User'|trans }} {{ 'Subscribers count'|trans }}
{{ loop.index }} @{{ user.login }} {{ user.subscribersCount }}
- - - - - - - - - {% for user in top_users %} - - - - - - {% endfor %} - -
#{{ 'User'|trans }}{{ 'Subscribers count'|trans }}
{{ loop.index }}@{{ user.login }}{{ user.subscribersCount }}
+{% block head_js %} + {{ parent() }} + + + +{% endblock %} + +{% block content %} + + +
{% endblock %} From c5f63d86cb4ad4213cf83334fd2602f72b43144c Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Fri, 26 Jun 2015 19:16:14 +0300 Subject: [PATCH 12/33] Last user subscribers events query optimized. --- .../Bundle/PointToolsBundle/Controller/UserController.php | 2 +- .../PointToolsBundle/Entity/SubscriptionEventRepository.php | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php index 8f442e3..588642e 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/UserController.php @@ -30,7 +30,7 @@ class UserController extends Controller return $this->render('SkobkinPointToolsBundle:User:show.html.twig', [ 'user' => $user, 'subscribers' => $em->getRepository('SkobkinPointToolsBundle:User')->findUserSubscribersById($user->getId()), - 'log' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getUserLastSubscriptionEventsById($user, 10), + 'log' => $em->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->getUserLastSubscribersEventsById($user, 10), 'avatar_url' => $userApi->getAvatarUrl($user, UserApi::AVATAR_SIZE_LARGE), ]); } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php index f17f01c..b12332c 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEventRepository.php @@ -29,7 +29,7 @@ class SubscriptionEventRepository extends EntityRepository * @param integer $limit * @return SubscriptionEvent[] */ - public function getUserLastSubscriptionEventsById(User $user, $limit) + public function getUserLastSubscribersEventsById(User $user, $limit) { if (!is_int($limit)) { throw new \InvalidArgumentException('$limit must be an integer'); @@ -38,7 +38,8 @@ class SubscriptionEventRepository extends EntityRepository $qb = $this->createQueryBuilder('se'); return $qb - ->select() + ->select(['se', 's']) + ->join('se.subscriber', 's') ->where('se.author = :author') ->orderBy('se.date', 'desc') ->setMaxResults($limit) From 4dfda2140181516a5186a8bd135c31aa107dac63 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Sat, 8 Aug 2015 12:22:35 +0300 Subject: [PATCH 13/33] getUserSubscriptions methods in UserApi. --- .../PointToolsBundle/Service/UserApi.php | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php index 5de211f..4fca586 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php @@ -78,6 +78,40 @@ class UserApi extends AbstractApi return $users; } + /** + * Get user subscriptions by user login + * + * @param string $login + * @return User[] + */ + public function getUserSubscriptionsByLogin($login) + { + $usersList = $this->getGetRequestData('/api/user/' . $login . '/subscriptions', [], true); + + $users = $this->getUsersFromList($usersList); + + return $users; + } + + /** + * Get user subscriptions by user id + * + * @param int $id + * @return User[] + */ + public function getUserSubscriptionsById($id) + { + if (!is_numeric($id)) { + throw new \InvalidArgumentException('$id must be an integer'); + } + + $usersList = $this->getGetRequestData('/api/user/id/' . (int) $id . '/subscriptions', [], true); + + $users = $this->getUsersFromList($usersList); + + return $users; + } + /** * @return User[] */ From b135ee0cec193a945fce1dd13431d6e38c844e74 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Sat, 8 Aug 2015 12:25:24 +0300 Subject: [PATCH 14/33] TODO in template. --- .../Bundle/PointToolsBundle/Resources/views/User/show.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig index 75f5842..7630503 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/User/show.html.twig @@ -66,7 +66,7 @@ - {# Use DateTime helper: https://sonata-project.org/bundles/intl/master/doc/reference/datetime.html #} + {# @todo Use DateTime helper: https://sonata-project.org/bundles/intl/master/doc/reference/datetime.html #} {{ event.date|date('d F Y H:i:s') }} From de234f1290153a5683c19adbb6b3fe6d562fef30 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 7 Sep 2015 11:29:03 +0300 Subject: [PATCH 15/33] composer update (security fixes) --- composer.lock | 60 +++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/composer.lock b/composer.lock index 1c65f54..8c38b7f 100644 --- a/composer.lock +++ b/composer.lock @@ -8,16 +8,16 @@ "packages": [ { "name": "doctrine/annotations", - "version": "v1.2.4", + "version": "v1.2.7", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "b5202eb9e83f8db52e0e58867e0a46e63be8332e" + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/b5202eb9e83f8db52e0e58867e0a46e63be8332e", - "reference": "b5202eb9e83f8db52e0e58867e0a46e63be8332e", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", "shasum": "" }, "require": { @@ -72,20 +72,20 @@ "docblock", "parser" ], - "time": "2014-12-23 22:40:37" + "time": "2015-08-31 12:32:49" }, { "name": "doctrine/cache", - "version": "v1.4.1", + "version": "v1.4.2", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "c9eadeb743ac6199f7eec423cb9426bc518b7b03" + "reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/c9eadeb743ac6199f7eec423cb9426bc518b7b03", - "reference": "c9eadeb743ac6199f7eec423cb9426bc518b7b03", + "url": "https://api.github.com/repos/doctrine/cache/zipball/8c434000f420ade76a07c64cbe08ca47e5c101ca", + "reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca", "shasum": "" }, "require": { @@ -142,7 +142,7 @@ "cache", "caching" ], - "time": "2015-04-15 00:11:59" + "time": "2015-08-31 12:36:41" }, { "name": "doctrine/collections", @@ -212,16 +212,16 @@ }, { "name": "doctrine/common", - "version": "v2.5.0", + "version": "v2.5.1", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3" + "reference": "0009b8f0d4a917aabc971fb089eba80e872f83f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/cd8daf2501e10c63dced7b8b9b905844316ae9d3", - "reference": "cd8daf2501e10c63dced7b8b9b905844316ae9d3", + "url": "https://api.github.com/repos/doctrine/common/zipball/0009b8f0d4a917aabc971fb089eba80e872f83f9", + "reference": "0009b8f0d4a917aabc971fb089eba80e872f83f9", "shasum": "" }, "require": { @@ -281,7 +281,7 @@ "persistence", "spl" ], - "time": "2015-04-02 19:55:44" + "time": "2015-08-31 13:00:22" }, { "name": "doctrine/dbal", @@ -698,7 +698,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/43619782df4e9ae6be6b24557f0429471eeee848", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/4e85ba638acf62e1afe735f94b0c51aa971f8773", "reference": "abb87d84ed21fd30c27bd3b52252a495a36d32fb", "shasum": "" }, @@ -755,16 +755,16 @@ }, { "name": "doctrine/orm", - "version": "v2.4.7", + "version": "v2.4.8", "source": { "type": "git", "url": "https://github.com/doctrine/doctrine2.git", - "reference": "2bc4ff3cab2ae297bcd05f2e619d42e6a7ca9e68" + "reference": "5aedac1e5c5caaeac14798822c70325dc242d467" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/2bc4ff3cab2ae297bcd05f2e619d42e6a7ca9e68", - "reference": "2bc4ff3cab2ae297bcd05f2e619d42e6a7ca9e68", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/5aedac1e5c5caaeac14798822c70325dc242d467", + "reference": "5aedac1e5c5caaeac14798822c70325dc242d467", "shasum": "" }, "require": { @@ -824,7 +824,7 @@ "database", "orm" ], - "time": "2014-12-16 13:45:01" + "time": "2015-08-31 13:19:01" }, { "name": "guzzle/guzzle", @@ -1738,7 +1738,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/symfony/zipball/a0af4ef1bfe8788288a2e5f95635134cba3ea842", + "url": "https://api.github.com/repos/symfony/symfony/zipball/ae643240bb7d2c6b5b8bb789965ba5413476252d", "reference": "7493c2bef54fb818c5304bdd9d2194890b839422", "shasum": "" }, @@ -1904,25 +1904,29 @@ }, { "name": "twig/twig", - "version": "v1.18.1", + "version": "v1.21.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "9f70492f44398e276d1b81c1b43adfe6751c7b7f" + "reference": "ca8d3aa90b6a01c82e07909fe815d6b443e75a23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9f70492f44398e276d1b81c1b43adfe6751c7b7f", - "reference": "9f70492f44398e276d1b81c1b43adfe6751c7b7f", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/ca8d3aa90b6a01c82e07909fe815d6b443e75a23", + "reference": "ca8d3aa90b6a01c82e07909fe815d6b443e75a23", "shasum": "" }, "require": { "php": ">=5.2.7" }, + "require-dev": { + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~2.7" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.18-dev" + "dev-master": "1.21-dev" } }, "autoload": { @@ -1957,7 +1961,7 @@ "keywords": [ "templating" ], - "time": "2015-04-19 08:30:27" + "time": "2015-08-26 08:58:31" }, { "name": "zendframework/zend-json", From f351fc22ffb0b2dd23ed7b74693f86e06729d876 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 1 Oct 2015 21:05:11 +0300 Subject: [PATCH 16/33] API exceptions. --- .../Service/Exceptions/ApiException.php | 9 +++++++++ .../Exceptions/SubscriptionManagerException.php | 9 +++++++++ .../Bundle/PointToolsBundle/Service/UserApi.php | 15 +++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/ApiException.php create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/SubscriptionManagerException.php diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/ApiException.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/ApiException.php new file mode 100644 index 0000000..0eb8fd5 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/ApiException.php @@ -0,0 +1,9 @@ +setId((int) $userData['id']); $this->em->persist($user); + + try { + $this->em->flush(); + } catch (\Exception $e) { + throw new ApiException(sprintf('Error while flushing new user [%d] %s', $user->getId(), $user->getLogin()), 0, $e); + } } // Updating data @@ -146,12 +153,16 @@ class UserApi extends AbstractApi $user->setName($userData['name']); } + try { + $this->em->flush(); + } catch (\Exception $e) { + throw new ApiException(sprintf('Error while flushing changes for [%d] %s', $user->getId(), $user->getLogin()), 0, $e); + } + $resultUsers[] = $user; } } - $this->em->flush(); - return $resultUsers; } From f7bbe3ebedcdbc594eb81d84ccdad7d506fc2461 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 1 Oct 2015 21:15:49 +0300 Subject: [PATCH 17/33] Temporary #21 bugfix. --- .../Version20151001210600.php | 34 +++++++++++++++++++ .../Bundle/PointToolsBundle/Entity/User.php | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 app/DoctrineMigrations/Version20151001210600.php diff --git a/app/DoctrineMigrations/Version20151001210600.php b/app/DoctrineMigrations/Version20151001210600.php new file mode 100644 index 0000000..db59780 --- /dev/null +++ b/app/DoctrineMigrations/Version20151001210600.php @@ -0,0 +1,34 @@ +abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.'); + + $this->addSql('DROP INDEX uniq_338adfc4aa08cb10'); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + // this down() migration is auto-generated, please modify it to your needs + $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.'); + + $this->addSql('CREATE UNIQUE INDEX uniq_338adfc4aa08cb10 ON users.users (login)'); + } +} diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php index 35ee886..f327ea1 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php @@ -25,7 +25,7 @@ class User /** * @var string * - * @ORM\Column(name="login", type="string", length=255, nullable=false, unique=true) + * @ORM\Column(name="login", type="string", length=255, nullable=false) */ private $login; From 27d6ee85595b1a7b1c060e2202f00570c2b9fd68 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 1 Oct 2015 21:28:57 +0300 Subject: [PATCH 18/33] Doctrine Migrations Bundle composer.json fix. --- composer.json | 4 ++-- composer.lock | 41 ++++++++++++++++++++--------------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/composer.json b/composer.json index 058368b..347e575 100644 --- a/composer.json +++ b/composer.json @@ -21,8 +21,8 @@ "incenteev/composer-parameter-handler": "~2.0", "misd/guzzle-bundle": "~1.0", "doctrine/migrations": "1.0.*@dev", - "doctrine/doctrine-migrations-bundle": "2.1.*@dev", - "ob/highcharts-bundle": "^1.2" + "ob/highcharts-bundle": "^1.2", + "doctrine/doctrine-migrations-bundle": "^1.0" }, "require-dev": { "sensio/generator-bundle": "~2.3" diff --git a/composer.lock b/composer.lock index 8c38b7f..8ebdb2d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "bfe197a5b9e16d562aba89cb97f380bf", + "hash": "3ea840c67fb58fde0c60ae6cf30da8c0", + "content-hash": "1344636f00611d01247e8c129d44e2f0", "packages": [ { "name": "doctrine/annotations", @@ -510,34 +511,33 @@ }, { "name": "doctrine/doctrine-migrations-bundle", - "version": "dev-master", - "target-dir": "Doctrine/Bundle/MigrationsBundle", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineMigrationsBundle.git", - "reference": "1e8cd4415bd2f893eb828216b529a75e8b61d579" + "reference": "93ec729e3f2f1bb882904cce9d2c1dde6f139ec8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/861d3564c03b3867845ffd87d9b19f49dc673c69", - "reference": "1e8cd4415bd2f893eb828216b529a75e8b61d579", + "url": "https://api.github.com/repos/doctrine/DoctrineMigrationsBundle/zipball/93ec729e3f2f1bb882904cce9d2c1dde6f139ec8", + "reference": "93ec729e3f2f1bb882904cce9d2c1dde6f139ec8", "shasum": "" }, "require": { "doctrine/doctrine-bundle": "~1.0", - "doctrine/migrations": "~1.0@dev", + "doctrine/migrations": "~1.0", "php": ">=5.3.2", "symfony/framework-bundle": "~2.3|~3.0" }, "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev" + "dev-master": "1.1-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Bundle\\MigrationsBundle": "" + "psr-4": { + "Doctrine\\Bundle\\MigrationsBundle\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -565,7 +565,7 @@ "migrations", "schema" ], - "time": "2015-05-06 08:32:15" + "time": "2015-09-29 10:07:00" }, { "name": "doctrine/inflector", @@ -698,7 +698,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/4e85ba638acf62e1afe735f94b0c51aa971f8773", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/d196ddc229f50c66c5a015c158adb78a2dfb4351", "reference": "abb87d84ed21fd30c27bd3b52252a495a36d32fb", "shasum": "" }, @@ -1552,12 +1552,12 @@ "version": "v2.6.1", "source": { "type": "git", - "url": "https://github.com/symfony/AsseticBundle.git", + "url": "https://github.com/symfony/assetic-bundle.git", "reference": "422b0add2110f0cf9bc7a873a386ea053f4a89f0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/AsseticBundle/zipball/422b0add2110f0cf9bc7a873a386ea053f4a89f0", + "url": "https://api.github.com/repos/symfony/assetic-bundle/zipball/422b0add2110f0cf9bc7a873a386ea053f4a89f0", "reference": "422b0add2110f0cf9bc7a873a386ea053f4a89f0", "shasum": "" }, @@ -1617,12 +1617,12 @@ "version": "v2.7.1", "source": { "type": "git", - "url": "https://github.com/symfony/MonologBundle.git", + "url": "https://github.com/symfony/monolog-bundle.git", "reference": "9320b6863404c70ebe111e9040dab96f251de7ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/MonologBundle/zipball/9320b6863404c70ebe111e9040dab96f251de7ac", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/9320b6863404c70ebe111e9040dab96f251de7ac", "reference": "9320b6863404c70ebe111e9040dab96f251de7ac", "shasum": "" }, @@ -1676,12 +1676,12 @@ "version": "v2.3.8", "source": { "type": "git", - "url": "https://github.com/symfony/SwiftmailerBundle.git", + "url": "https://github.com/symfony/swiftmailer-bundle.git", "reference": "970b13d01871207e81d17b17ddda025e7e21e797" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/SwiftmailerBundle/zipball/970b13d01871207e81d17b17ddda025e7e21e797", + "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/970b13d01871207e81d17b17ddda025e7e21e797", "reference": "970b13d01871207e81d17b17ddda025e7e21e797", "shasum": "" }, @@ -1738,7 +1738,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/symfony/zipball/ae643240bb7d2c6b5b8bb789965ba5413476252d", + "url": "https://api.github.com/repos/symfony/symfony/zipball/4b0e4c4ee0a1e4f8a934d919de1c318c48643969", "reference": "7493c2bef54fb818c5304bdd9d2194890b839422", "shasum": "" }, @@ -2127,8 +2127,7 @@ "minimum-stability": "stable", "stability-flags": { "symfony/symfony": 20, - "doctrine/migrations": 20, - "doctrine/doctrine-migrations-bundle": 20 + "doctrine/migrations": 20 }, "prefer-stable": false, "prefer-lowest": false, From a60bce3b23fc6bd7da8937748b6bc111c2b58b22 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 1 Oct 2015 21:31:48 +0300 Subject: [PATCH 19/33] Symfony stable 2.7.x branch. --- composer.json | 2 +- composer.lock | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 347e575..d5f5f3f 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ }, "require": { "php": ">=5.3.3", - "symfony/symfony": "2.7.x-dev", + "symfony/symfony": "2.7.*", "doctrine/orm": "~2.2,>=2.2.3,<2.5", "doctrine/dbal": "<2.5", "doctrine/doctrine-bundle": "~1.4", diff --git a/composer.lock b/composer.lock index 8ebdb2d..5baf850 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "3ea840c67fb58fde0c60ae6cf30da8c0", - "content-hash": "1344636f00611d01247e8c129d44e2f0", + "hash": "2730cf99a45485bcd85fb5e0eed3a41c", + "content-hash": "0da9c81dcc1cb8e886339580c46ccda9", "packages": [ { "name": "doctrine/annotations", @@ -1730,23 +1730,23 @@ }, { "name": "symfony/symfony", - "version": "2.7.x-dev", + "version": "v2.7.5", "source": { "type": "git", "url": "https://github.com/symfony/symfony.git", - "reference": "7493c2bef54fb818c5304bdd9d2194890b839422" + "reference": "619528a274647cffc1792063c3ea04c4fa8266a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/symfony/zipball/4b0e4c4ee0a1e4f8a934d919de1c318c48643969", - "reference": "7493c2bef54fb818c5304bdd9d2194890b839422", + "url": "https://api.github.com/repos/symfony/symfony/zipball/619528a274647cffc1792063c3ea04c4fa8266a0", + "reference": "619528a274647cffc1792063c3ea04c4fa8266a0", "shasum": "" }, "require": { - "doctrine/common": "~2.3", + "doctrine/common": "~2.4", "php": ">=5.3.9", "psr/log": "~1.0", - "twig/twig": "~1.18" + "twig/twig": "~1.20|~2.0" }, "replace": { "symfony/asset": "self.version", @@ -1796,9 +1796,9 @@ }, "require-dev": { "doctrine/data-fixtures": "1.0.*", - "doctrine/dbal": "~2.2", + "doctrine/dbal": "~2.4", "doctrine/doctrine-bundle": "~1.2", - "doctrine/orm": "~2.2,>=2.2.3", + "doctrine/orm": "~2.4,>=2.4.5", "egulias/email-validator": "~1.2", "ircmaxell/password-compat": "~1.0", "monolog/monolog": "~1.11", @@ -1848,7 +1848,7 @@ "keywords": [ "framework" ], - "time": "2015-05-30 17:16:04" + "time": "2015-09-25 11:16:52" }, { "name": "twig/extensions", @@ -2126,7 +2126,6 @@ "aliases": [], "minimum-stability": "stable", "stability-flags": { - "symfony/symfony": 20, "doctrine/migrations": 20 }, "prefer-stable": false, From 0685e6d8f959aeaf464202970bb92ecbd609fd04 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 1 Oct 2015 21:45:16 +0300 Subject: [PATCH 20/33] Migration fix. --- app/DoctrineMigrations/Version20151001210600.php | 2 +- src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php | 2 +- .../Bundle/PointToolsBundle/Entity/SubscriptionEvent.php | 2 +- src/Skobkin/Bundle/PointToolsBundle/Entity/User.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/DoctrineMigrations/Version20151001210600.php b/app/DoctrineMigrations/Version20151001210600.php index db59780..101a533 100644 --- a/app/DoctrineMigrations/Version20151001210600.php +++ b/app/DoctrineMigrations/Version20151001210600.php @@ -18,7 +18,7 @@ class Version20151001210600 extends AbstractMigration // this up() migration is auto-generated, please modify it to your needs $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'postgresql', 'Migration can only be executed safely on \'postgresql\'.'); - $this->addSql('DROP INDEX uniq_338adfc4aa08cb10'); + $this->addSql('DROP INDEX users.uniq_338adfc4aa08cb10'); } /** diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php index 588c5e4..dfd4c31 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/Subscription.php @@ -7,7 +7,7 @@ use Doctrine\ORM\Mapping as ORM; /** * Subscription * - * @ORM\Table(name="subscriptions.subscriptions", uniqueConstraints={ + * @ORM\Table(name="subscriptions.subscriptions", schema="subscriptions", uniqueConstraints={ * @ORM\UniqueConstraint(name="subscription_unique", columns={"author_id", "subscriber_id"})} * ) * @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Entity\SubscriptionRepository") diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php index 1c1c4a2..22c4a4e 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/SubscriptionEvent.php @@ -7,7 +7,7 @@ use Doctrine\ORM\Mapping as ORM; /** * SubscriptionEvent * - * @ORM\Table(name="subscriptions.log", indexes={ + * @ORM\Table(name="subscriptions.log", schema="subscriptions", indexes={ * @ORM\Index(name="author_idx", columns={"author_id"}), * @ORM\Index(name="subscriber_idx", columns={"subscriber_id"}), * @ORM\Index(name="date_idx", columns={"date"}) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php index f327ea1..2b4dbd5 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php @@ -8,7 +8,7 @@ use Doctrine\ORM\Mapping as ORM; /** * User * - * @ORM\Table(name="users.users") + * @ORM\Table(name="users.users", schema="users") * @ORM\Entity(repositoryClass="Skobkin\Bundle\PointToolsBundle\Entity\UserRepository") * @ORM\HasLifecycleCallbacks */ From dfc7fb70e4b60cb9e960394b9efaf896d32976cf Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 1 Oct 2015 22:14:30 +0300 Subject: [PATCH 21/33] User without login flushing fix. --- src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php index d8476e2..09d5dc2 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php @@ -135,13 +135,16 @@ class UserApi extends AbstractApi if (!$user) { $user = new User(); - $user->setId((int) $userData['id']); + $user + ->setId((int) $userData['id']) + ->setLogin($userData['login']) + ; $this->em->persist($user); try { $this->em->flush(); } catch (\Exception $e) { - throw new ApiException(sprintf('Error while flushing new user [%d] %s', $user->getId(), $user->getLogin()), 0, $e); + throw new ApiException(sprintf('Error while flushing new user [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e); } } @@ -156,7 +159,7 @@ class UserApi extends AbstractApi try { $this->em->flush(); } catch (\Exception $e) { - throw new ApiException(sprintf('Error while flushing changes for [%d] %s', $user->getId(), $user->getLogin()), 0, $e); + throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e); } $resultUsers[] = $user; From f91f106b5e71bda322d420dff517d9bc8c3a3ea2 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 03:49:01 +0300 Subject: [PATCH 22/33] Adding Yandex.Metrika. --- app/Resources/views/base.html.twig | 1 + app/Resources/views/counters.html.twig | 28 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 app/Resources/views/counters.html.twig diff --git a/app/Resources/views/base.html.twig b/app/Resources/views/base.html.twig index a5731f9..875f8f1 100644 --- a/app/Resources/views/base.html.twig +++ b/app/Resources/views/base.html.twig @@ -58,4 +58,5 @@

+ {% include 'counters.html.twig' %} {% endblock %} \ No newline at end of file diff --git a/app/Resources/views/counters.html.twig b/app/Resources/views/counters.html.twig new file mode 100644 index 0000000..a438860 --- /dev/null +++ b/app/Resources/views/counters.html.twig @@ -0,0 +1,28 @@ + + + + \ No newline at end of file From 23b51ea98ca6260cb83151a660620eb9e1095302 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 03:56:16 +0300 Subject: [PATCH 23/33] User constructor now recieving id, login and name for faster creation. --- src/Skobkin/Bundle/PointToolsBundle/Entity/User.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php index 2b4dbd5..038dee0 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/User.php @@ -71,8 +71,17 @@ class User private $newSubscriberEvents; - public function __construct() + /** + * @param int $id + * @param string $login + * @param string $name + */ + public function __construct($id = null, $login = null, $name = null) { + $this->id = $id; + $this->login = $login; + $this->name = $name; + $this->subscribers = new ArrayCollection(); $this->subscriptions = new ArrayCollection(); $this->newSubscriberEvents = new ArrayCollection(); From c3191d82e618f7313e88a91a863b9fba2f591397 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 03:59:22 +0300 Subject: [PATCH 24/33] New UserApi methods for getting single user from API. Some refactoring. New API exceptions. --- .../Exceptions/InvalidResponseException.php | 9 + .../Exceptions/UserNotFoundException.php | 47 ++++ .../PointToolsBundle/Service/UserApi.php | 205 ++++++++++++++---- 3 files changed, 213 insertions(+), 48 deletions(-) create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/InvalidResponseException.php create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/UserNotFoundException.php diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/InvalidResponseException.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/InvalidResponseException.php new file mode 100644 index 0000000..101b7ee --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Exceptions/InvalidResponseException.php @@ -0,0 +1,9 @@ +userId = $userId; + $this->login = $login; + } + + /** + * Returns ID of user which was not found + * + * @return int + */ + public function getUserId() + { + return $this->userId; + } + + /** + * @return string + */ + public function getLogin() + { + return $this->login; + } +} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php index 09d5dc2..02b77db 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php @@ -5,19 +5,19 @@ namespace Skobkin\Bundle\PointToolsBundle\Service; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityRepository; +use Guzzle\Http\Exception\ClientErrorResponseException; use Guzzle\Service\Client; use Skobkin\Bundle\PointToolsBundle\Entity\User; use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\ApiException; +use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\InvalidResponseException; +use Skobkin\Bundle\PointToolsBundle\Service\Exceptions\UserNotFoundException; +use Symfony\Component\HttpFoundation\Response; /** * Basic Point.im user API functions from /api/user/* */ class UserApi extends AbstractApi { - const PATH_USER_INFO = '/api/user/%s'; - const PATH_USER_SUBSCRIPTIONS = '/api/user/%s/subscriptions'; - const PATH_USER_SUBSCRIBERS = '/api/user/%s/subscribers'; - const AVATAR_SIZE_SMALL = '24'; const AVATAR_SIZE_MEDIUM = '40'; const AVATAR_SIZE_LARGE = '80'; @@ -25,7 +25,7 @@ class UserApi extends AbstractApi /** * @var string Base URL for user avatars */ - protected $avatarsBaseUrl = 'point.im/avatar/'; + protected $avatarsBaseUrl = '//point.im/avatar/'; /** * @var EntityManager @@ -50,21 +50,33 @@ class UserApi extends AbstractApi * * @param string $login * @return User[] + * @throws ApiException + * @throws InvalidResponseException + * @throws UserNotFoundException */ public function getUserSubscribersByLogin($login) { - $usersList = $this->getGetRequestData('/api/user/' . $login . '/subscribers', [], true); + try { + $usersList = $this->getGetRequestData('/api/user/'.$login.'/subscribers', [], true); + } catch (ClientErrorResponseException $e) { + if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { + throw new UserNotFoundException('User not found', 0, $e, null, $login); + } else { + throw $e; + } + } - $users = $this->getUsersFromList($usersList); - - return $users; + return $this->getUsersFromList($usersList); } /** * Get user subscribers by user id * - * @param int $id + * @param $id * @return User[] + * @throws ApiException + * @throws InvalidResponseException + * @throws UserNotFoundException */ public function getUserSubscribersById($id) { @@ -72,11 +84,17 @@ class UserApi extends AbstractApi throw new \InvalidArgumentException('$id must be an integer'); } - $usersList = $this->getGetRequestData('/api/user/id/' . (int) $id . '/subscribers', [], true); + try { + $usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscribers', [], true); + } catch (ClientErrorResponseException $e) { + if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { + throw new UserNotFoundException('User not found', 0, $e, $id); + } else { + throw $e; + } + } - $users = $this->getUsersFromList($usersList); - - return $users; + return $this->getUsersFromList($usersList); } /** @@ -84,21 +102,33 @@ class UserApi extends AbstractApi * * @param string $login * @return User[] + * @throws ApiException + * @throws InvalidResponseException + * @throws UserNotFoundException */ public function getUserSubscriptionsByLogin($login) { - $usersList = $this->getGetRequestData('/api/user/' . $login . '/subscriptions', [], true); + try { + $usersList = $this->getGetRequestData('/api/user/'.$login.'/subscriptions', [], true); + } catch (ClientErrorResponseException $e) { + if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { + throw new UserNotFoundException('User not found', 0, $e, null, $login); + } else { + throw $e; + } + } - $users = $this->getUsersFromList($usersList); - - return $users; + return $this->getUsersFromList($usersList); } /** * Get user subscriptions by user id * - * @param int $id + * @param $id * @return User[] + * @throws ApiException + * @throws InvalidResponseException + * @throws UserNotFoundException */ public function getUserSubscriptionsById($id) { @@ -106,15 +136,97 @@ class UserApi extends AbstractApi throw new \InvalidArgumentException('$id must be an integer'); } - $usersList = $this->getGetRequestData('/api/user/id/' . (int) $id . '/subscriptions', [], true); + try { + $usersList = $this->getGetRequestData('/api/user/id/'.(int) $id.'/subscriptions', [], true); + } catch (ClientErrorResponseException $e) { + if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { + throw new UserNotFoundException('User not found', 0, $e, $id); + } else { + throw $e; + } + } - $users = $this->getUsersFromList($usersList); - - return $users; + return $this->getUsersFromList($usersList); } /** + * Get user by id + * + * @param $id + * @return User + * @throws UserNotFoundException + * @throws ClientErrorResponseException + */ + public function getUserById($id) + { + if (!is_numeric($id)) { + throw new \InvalidArgumentException('$id must be an integer'); + } + + try { + $userInfo = $this->getGetRequestData('/api/user/id/'.(int) $id, [], true); + } catch (ClientErrorResponseException $e) { + if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { + throw new UserNotFoundException('User not found', 0, $e, $id); + } else { + throw $e; + } + } + + return $this->getUserFromUserInfo($userInfo); + } + + /** + * Finds and updates or create new user from API response data + * + * @param array $userInfo + * @return User + * @throws ApiException + * @throws InvalidResponseException + */ + public function getUserFromUserInfo(array $userInfo) + { + if (!is_array($userInfo)) { + throw new \InvalidArgumentException('$userInfo must be an array'); + } + + /** @var EntityRepository $userRepo */ + $userRepo = $this->em->getRepository('SkobkinPointToolsBundle:User'); + + // @todo Return ID existance check when @ap-Codkelden will fix this API behaviour + if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) { + /** @var User $user */ + if (null === ($user = $userRepo->find($userInfo['id']))) { + // Creating new user + $user = new User($userInfo['id']); + $this->em->persist($user); + } + + // Updating data + $user + ->setLogin($userInfo['login']) + ->setName($userInfo['name']) + ; + + try { + $this->em->flush($user); + } catch (\Exception $e) { + throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e); + } + + return $user; + } + + throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.'); + } + + /** + * Get array of User objects from API response containing user list + * + * @param array $users * @return User[] + * @throws ApiException + * @throws InvalidResponseException */ private function getUsersFromList(array $users = []) { @@ -125,44 +237,33 @@ class UserApi extends AbstractApi /** @var EntityRepository $userRepo */ $userRepo = $this->em->getRepository('SkobkinPointToolsBundle:User'); + /** @var User[] $resultUsers */ $resultUsers = []; - foreach ($users as $userData) { - if (array_key_exists('id', $userData) && array_key_exists('login', $userData) && array_key_exists('name', $userData) && is_numeric($userData['id'])) { + foreach ($users as $userInfo) { + if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) { // @todo Optimize with prehashed id's list - $user = $userRepo->findOneBy(['id' => $userData['id']]); - - if (!$user) { - $user = new User(); - $user - ->setId((int) $userData['id']) - ->setLogin($userData['login']) - ; + if (null === ($user = $userRepo->find($userInfo['id']))) { + $user = new User((int) $userInfo['id']); $this->em->persist($user); - - try { - $this->em->flush(); - } catch (\Exception $e) { - throw new ApiException(sprintf('Error while flushing new user [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e); - } } // Updating data - if ($user->getLogin() !== $userData['login']) { - $user->setLogin($userData['login']); - } - if ($user->getName() !== $userData['name']) { - $user->setName($userData['name']); - } + $user + ->setLogin($userInfo['login']) + ->setName($userInfo['name']) + ; try { - $this->em->flush(); + $this->em->flush($user); } catch (\Exception $e) { throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $user->getId(), $user->getLogin(), $e->getMessage()), 0, $e); } $resultUsers[] = $user; + } else { + throw new InvalidResponseException('Invalid API response. Mandatory fields do not exist.'); } } @@ -170,10 +271,18 @@ class UserApi extends AbstractApi } /** - * @param $login + * Creates avatar with specified size URL for user + * + * @param User $user + * @param int $size + * @return string */ public function getAvatarUrl(User $user, $size) { - return ($this->useHttps ? 'https://' : 'http://') . $this->avatarsBaseUrl . $user->getLogin() . '/' . $size; + if (!in_array($size, [self::AVATAR_SIZE_SMALL, self::AVATAR_SIZE_MEDIUM, self::AVATAR_SIZE_LARGE], true)) { + throw new \InvalidArgumentException('Avatar size must be one of restricted variants. See UserApi class AVATAR_SIZE_* constants.'); + } + + return $this->avatarsBaseUrl.$user->getLogin().'/'.$size; } } From f8bd95360e044bab3eaf316b2f1009a2df4d2459 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 04:02:06 +0300 Subject: [PATCH 25/33] Common User repository via class property. --- .../Bundle/PointToolsBundle/Service/UserApi.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php index 02b77db..12c477e 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php @@ -32,12 +32,18 @@ class UserApi extends AbstractApi */ protected $em; + /** + * @var EntityRepository + */ + protected $userRepository; + public function __construct(Client $httpClient, $https = true, $baseUrl = null, EntityManagerInterface $entityManager) { parent::__construct($httpClient, $https, $baseUrl); $this->em = $entityManager; + $this->userRepository = $this->em->getRepository('SkobkinPointToolsBundle:User'); } public function getName() @@ -190,13 +196,10 @@ class UserApi extends AbstractApi throw new \InvalidArgumentException('$userInfo must be an array'); } - /** @var EntityRepository $userRepo */ - $userRepo = $this->em->getRepository('SkobkinPointToolsBundle:User'); - // @todo Return ID existance check when @ap-Codkelden will fix this API behaviour if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) { /** @var User $user */ - if (null === ($user = $userRepo->find($userInfo['id']))) { + if (null === ($user = $this->userRepository->find($userInfo['id']))) { // Creating new user $user = new User($userInfo['id']); $this->em->persist($user); @@ -234,9 +237,6 @@ class UserApi extends AbstractApi throw new \InvalidArgumentException('$users must be an array'); } - /** @var EntityRepository $userRepo */ - $userRepo = $this->em->getRepository('SkobkinPointToolsBundle:User'); - /** @var User[] $resultUsers */ $resultUsers = []; @@ -244,7 +244,7 @@ class UserApi extends AbstractApi if (array_key_exists('id', $userInfo) && array_key_exists('login', $userInfo) && array_key_exists('name', $userInfo) && is_numeric($userInfo['id'])) { // @todo Optimize with prehashed id's list - if (null === ($user = $userRepo->find($userInfo['id']))) { + if (null === ($user = $this->userRepository->find($userInfo['id']))) { $user = new User((int) $userInfo['id']); $this->em->persist($user); } From 295b08961667ec2527ebf9cd8574148526447b9f Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 04:08:01 +0300 Subject: [PATCH 26/33] getUserByLogin() method in UserApi. --- .../PointToolsBundle/Service/UserApi.php | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php index 12c477e..c09e70e 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php @@ -156,7 +156,30 @@ class UserApi extends AbstractApi } /** - * Get user by id + * Get single user by login + * + * @param string $login + * @return User + * @throws UserNotFoundException + * @throws ClientErrorResponseException + */ + public function getUserByLogin($login) + { + try { + $userInfo = $this->getGetRequestData('/api/user/login/'.$login, [], true); + } catch (ClientErrorResponseException $e) { + if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { + throw new UserNotFoundException('User not found', 0, $e, null, $login); + } else { + throw $e; + } + } + + return $this->getUserFromUserInfo($userInfo); + } + + /** + * Get single user by id * * @param $id * @return User From 4bd570e910ddaea642d9c7af1615eca8c3b52e97 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 04:10:07 +0300 Subject: [PATCH 27/33] urlencode() for user logins in API requests. --- src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php index c09e70e..0da52af 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/UserApi.php @@ -63,7 +63,7 @@ class UserApi extends AbstractApi public function getUserSubscribersByLogin($login) { try { - $usersList = $this->getGetRequestData('/api/user/'.$login.'/subscribers', [], true); + $usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscribers', [], true); } catch (ClientErrorResponseException $e) { if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { throw new UserNotFoundException('User not found', 0, $e, null, $login); @@ -115,7 +115,7 @@ class UserApi extends AbstractApi public function getUserSubscriptionsByLogin($login) { try { - $usersList = $this->getGetRequestData('/api/user/'.$login.'/subscriptions', [], true); + $usersList = $this->getGetRequestData('/api/user/'.urlencode($login).'/subscriptions', [], true); } catch (ClientErrorResponseException $e) { if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { throw new UserNotFoundException('User not found', 0, $e, null, $login); @@ -166,7 +166,7 @@ class UserApi extends AbstractApi public function getUserByLogin($login) { try { - $userInfo = $this->getGetRequestData('/api/user/login/'.$login, [], true); + $userInfo = $this->getGetRequestData('/api/user/login/'.urlencode($login), [], true); } catch (ClientErrorResponseException $e) { if (Response::HTTP_NOT_FOUND === $e->getResponse()->getStatusCode()) { throw new UserNotFoundException('User not found', 0, $e, null, $login); @@ -306,6 +306,6 @@ class UserApi extends AbstractApi throw new \InvalidArgumentException('Avatar size must be one of restricted variants. See UserApi class AVATAR_SIZE_* constants.'); } - return $this->avatarsBaseUrl.$user->getLogin().'/'.$size; + return $this->avatarsBaseUrl.urlencode($user->getLogin()).'/'.$size; } } From 1461837405f0e38effaa6b88105fc0e0355e61e4 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 04:16:49 +0300 Subject: [PATCH 28/33] Last events link on the events counter on the main page. --- .../PointToolsBundle/Resources/views/Main/index.html.twig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig index 2a2c3c9..5274662 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/views/Main/index.html.twig @@ -26,7 +26,7 @@
{{ '24 hours events'|trans }}
-
{{ events_count }}
+
{% endblock %} From 49cfdc7266880b9ef013ab1b6d67c8d25cf4df3a Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 04:26:28 +0300 Subject: [PATCH 29/33] Second DoctrineMigrationsBundle composer.json fix. --- composer.json | 1 - composer.lock | 36 ++++++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index d5f5f3f..796d57b 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,6 @@ "sensio/framework-extra-bundle": "~3.0,>=3.0.2", "incenteev/composer-parameter-handler": "~2.0", "misd/guzzle-bundle": "~1.0", - "doctrine/migrations": "1.0.*@dev", "ob/highcharts-bundle": "^1.2", "doctrine/doctrine-migrations-bundle": "^1.0" }, diff --git a/composer.lock b/composer.lock index 5baf850..76f84ae 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "2730cf99a45485bcd85fb5e0eed3a41c", - "content-hash": "0da9c81dcc1cb8e886339580c46ccda9", + "hash": "4fe9c0fca61b1bf505886a29726aac87", + "content-hash": "7a96c16b3360dea55a684da6fa90d8fe", "packages": [ { "name": "doctrine/annotations", @@ -690,45 +690,47 @@ }, { "name": "doctrine/migrations", - "version": "dev-master", + "version": "v1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "abb87d84ed21fd30c27bd3b52252a495a36d32fb" + "reference": "d196ddc229f50c66c5a015c158adb78a2dfb4351" }, "dist": { "type": "zip", "url": "https://api.github.com/repos/doctrine/migrations/zipball/d196ddc229f50c66c5a015c158adb78a2dfb4351", - "reference": "abb87d84ed21fd30c27bd3b52252a495a36d32fb", + "reference": "d196ddc229f50c66c5a015c158adb78a2dfb4351", "shasum": "" }, "require": { - "doctrine/dbal": "~2.0", - "php": ">=5.3.2", + "doctrine/dbal": "~2.2", + "php": ">=5.4.0", "symfony/console": "~2.3", "symfony/yaml": "~2.3" }, - "conflict": { - "doctrine/orm": "<2.4" - }, "require-dev": { "doctrine/coding-standard": "dev-master", "doctrine/orm": "2.*", - "phpunit/phpunit": "~4.0", + "johnkary/phpunit-speedtrap": "~1.0@dev", + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "~4.7", "satooshi/php-coveralls": "0.6.*" }, "suggest": { "symfony/console": "to run the migration from the console" }, + "bin": [ + "bin/doctrine-migrations" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "v1.1.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\DBAL\\Migrations": "lib" + "psr-4": { + "Doctrine\\DBAL\\Migrations\\": "lib/Doctrine/DBAL/Migrations" } }, "notification-url": "https://packagist.org/downloads/", @@ -751,7 +753,7 @@ "database", "migrations" ], - "time": "2015-05-26 15:30:26" + "time": "2015-09-29 11:13:06" }, { "name": "doctrine/orm", @@ -2125,9 +2127,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "doctrine/migrations": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From a6628b43693d08dae05f75af621b20307fe94b09 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Mon, 5 Oct 2015 04:28:20 +0300 Subject: [PATCH 30/33] vendor update (including CVE-2015-5723 fix). --- app/SymfonyRequirements.php | 16 ++- app/check.php | 4 +- composer.lock | 205 +++++++++++++++++++----------------- 3 files changed, 122 insertions(+), 103 deletions(-) diff --git a/app/SymfonyRequirements.php b/app/SymfonyRequirements.php index caabe40..abaf0c1 100644 --- a/app/SymfonyRequirements.php +++ b/app/SymfonyRequirements.php @@ -638,20 +638,20 @@ class SymfonyRequirements extends RequirementCollection } $this->addRecommendation( - class_exists('Locale'), + extension_loaded('intl'), 'intl extension should be available', 'Install and enable the intl extension (used for validators).' ); - if (class_exists('Collator')) { + if (extension_loaded('intl')) { + // in some WAMP server installations, new Collator() returns null $this->addRecommendation( null !== new Collator('fr_FR'), 'intl extension should be correctly configured', 'The intl extension does not behave properly. This problem is typical on PHP 5.3.X x64 WIN builds.' ); - } - if (class_exists('Locale')) { + // check for compatible ICU versions (only done when you have the intl extension) if (defined('INTL_ICU_VERSION')) { $version = INTL_ICU_VERSION; } else { @@ -670,6 +670,14 @@ class SymfonyRequirements extends RequirementCollection 'intl ICU version should be at least 4+', 'Upgrade your intl extension with a newer ICU version (4+).' ); + + $this->addPhpIniRecommendation( + 'intl.error_level', + create_function('$cfgValue', 'return (int) $cfgValue === 0;'), + true, + 'intl.error_level should be 0 in php.ini', + 'Set "intl.error_level" to "0" in php.ini* to inhibit the messages when an error occurs in ICU functions.' + ); } $accelerator = diff --git a/app/check.php b/app/check.php index 90bad4a..60ae0a8 100755 --- a/app/check.php +++ b/app/check.php @@ -42,9 +42,9 @@ foreach ($symfonyRequirements->getRecommendations() as $req) { } if ($checkPassed) { - echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects', true); + echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects'); } else { - echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects', true); + echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects'); echo_title('Fix the following mandatory requirements', 'red'); diff --git a/composer.lock b/composer.lock index 76f84ae..6639c4a 100644 --- a/composer.lock +++ b/composer.lock @@ -349,16 +349,16 @@ }, { "name": "doctrine/doctrine-bundle", - "version": "v1.5.0", + "version": "v1.5.2", "source": { "type": "git", "url": "https://github.com/doctrine/DoctrineBundle.git", - "reference": "0b9e27037c4fdbad515ee5ec89842e9091a6480f" + "reference": "d63be7eb9a95d46720f7d6badac4e5bc2bcff2e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/0b9e27037c4fdbad515ee5ec89842e9091a6480f", - "reference": "0b9e27037c4fdbad515ee5ec89842e9091a6480f", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/d63be7eb9a95d46720f7d6badac4e5bc2bcff2e3", + "reference": "d63be7eb9a95d46720f7d6badac4e5bc2bcff2e3", "shasum": "" }, "require": { @@ -366,16 +366,16 @@ "doctrine/doctrine-cache-bundle": "~1.0", "jdorn/sql-formatter": "~1.1", "php": ">=5.3.2", - "symfony/console": "~2.3", - "symfony/doctrine-bridge": "~2.2", - "symfony/framework-bundle": "~2.3" + "symfony/console": "~2.3|~3.0", + "symfony/doctrine-bridge": "~2.2|~3.0", + "symfony/framework-bundle": "~2.3|~3.0" }, "require-dev": { "doctrine/orm": "~2.3", "phpunit/phpunit": "~4", "satooshi/php-coveralls": "~0.6.1", - "symfony/validator": "~2.2", - "symfony/yaml": "~2.2", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0", "twig/twig": "~1.10" }, "suggest": { @@ -385,7 +385,7 @@ "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "1.5.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -423,7 +423,7 @@ "orm", "persistence" ], - "time": "2015-05-28 12:27:15" + "time": "2015-08-31 14:47:06" }, { "name": "doctrine/doctrine-cache-bundle", @@ -925,17 +925,16 @@ }, { "name": "incenteev/composer-parameter-handler", - "version": "v2.1.0", - "target-dir": "Incenteev/ParameterHandler", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/Incenteev/ParameterHandler.git", - "reference": "143272a0a09c62616a3c8011fc165a10c6b35241" + "reference": "84a205fe80a46101607bafbc423019527893ddd0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/143272a0a09c62616a3c8011fc165a10c6b35241", - "reference": "143272a0a09c62616a3c8011fc165a10c6b35241", + "url": "https://api.github.com/repos/Incenteev/ParameterHandler/zipball/84a205fe80a46101607bafbc423019527893ddd0", + "reference": "84a205fe80a46101607bafbc423019527893ddd0", "shasum": "" }, "require": { @@ -954,8 +953,8 @@ } }, "autoload": { - "psr-0": { - "Incenteev\\ParameterHandler": "" + "psr-4": { + "Incenteev\\ParameterHandler\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -973,7 +972,7 @@ "keywords": [ "parameters management" ], - "time": "2013-12-07 10:10:39" + "time": "2015-06-03 08:27:03" }, { "name": "jdorn/sql-formatter", @@ -1027,35 +1026,39 @@ }, { "name": "kriswallsmith/assetic", - "version": "v1.2.1", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/kriswallsmith/assetic.git", - "reference": "b20efe38845d20458702f97f3ff625d80805897b" + "reference": "56cb5d6dec9e7a68a4da2fa89844f39d41092f31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/b20efe38845d20458702f97f3ff625d80805897b", - "reference": "b20efe38845d20458702f97f3ff625d80805897b", + "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/56cb5d6dec9e7a68a4da2fa89844f39d41092f31", + "reference": "56cb5d6dec9e7a68a4da2fa89844f39d41092f31", "shasum": "" }, "require": { "php": ">=5.3.1", "symfony/process": "~2.1" }, + "conflict": { + "twig/twig": "<1.12" + }, "require-dev": { "cssmin/cssmin": "*", "joliclic/javascript-packer": "*", "kamicane/packager": "*", - "leafo/lessphp": "*", - "leafo/scssphp": "*", - "leafo/scssphp-compass": "*", + "leafo/lessphp": "^0.3.7", + "leafo/scssphp": "*@dev", + "leafo/scssphp-compass": "*@dev", "mrclay/minify": "*", - "patchwork/jsqueeze": "~1.0", - "phpunit/phpunit": "~4", + "patchwork/jsqueeze": "~1.0|~2.0", + "phpunit/phpunit": "~4.8", "psr/log": "~1.0", "ptachoire/cssembed": "*", - "twig/twig": "~1.6" + "symfony/phpunit-bridge": "~2.7", + "twig/twig": "~1.8|~2.0" }, "suggest": { "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler", @@ -1068,7 +1071,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -1097,7 +1100,7 @@ "compression", "minification" ], - "time": "2014-12-12 05:04:05" + "time": "2015-08-31 19:07:16" }, { "name": "misd/guzzle-bundle", @@ -1173,16 +1176,16 @@ }, { "name": "monolog/monolog", - "version": "1.13.1", + "version": "1.17.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "c31a2c4e8db5da8b46c74cf275d7f109c0f249ac" + "reference": "0524c87587ab85bc4c2d6f5b41253ccb930a5422" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c31a2c4e8db5da8b46c74cf275d7f109c0f249ac", - "reference": "c31a2c4e8db5da8b46c74cf275d7f109c0f249ac", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/0524c87587ab85bc4c2d6f5b41253ccb930a5422", + "reference": "0524c87587ab85bc4c2d6f5b41253ccb930a5422", "shasum": "" }, "require": { @@ -1193,12 +1196,14 @@ "psr/log-implementation": "1.0.0" }, "require-dev": { - "aws/aws-sdk-php": "~2.4, >2.4.8", + "aws/aws-sdk-php": "^2.4.9", "doctrine/couchdb": "~1.0@dev", "graylog2/gelf-php": "~1.0", - "phpunit/phpunit": "~4.0", - "raven/raven": "~0.5", - "ruflin/elastica": "0.90.*", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "raven/raven": "~0.11", + "ruflin/elastica": ">=0.90 <3.0", "swiftmailer/swiftmailer": "~5.3", "videlalvaro/php-amqplib": "~2.4" }, @@ -1208,6 +1213,7 @@ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", "ext-mongo": "Allow sending log messages to a MongoDB server", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "php-console/php-console": "Allow sending log messages to Google Chrome", "raven/raven": "Allow sending log messages to a Sentry server", "rollbar/rollbar": "Allow sending log messages to Rollbar", "ruflin/elastica": "Allow sending log messages to an Elastic Search server", @@ -1216,7 +1222,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.13.x-dev" + "dev-master": "1.16.x-dev" } }, "autoload": { @@ -1242,7 +1248,7 @@ "logging", "psr-3" ], - "time": "2015-03-09 09:58:04" + "time": "2015-08-31 09:17:37" }, { "name": "ob/highcharts-bundle", @@ -1339,22 +1345,22 @@ }, { "name": "sensio/distribution-bundle", - "version": "v3.0.25", + "version": "v3.0.31", "target-dir": "Sensio/Bundle/DistributionBundle", "source": { "type": "git", "url": "https://github.com/sensiolabs/SensioDistributionBundle.git", - "reference": "01931139b0f067a4016d5d56e82c2b3086533b89" + "reference": "3a900814bd57bf20f9453ae81ff8772bc95d7fff" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/01931139b0f067a4016d5d56e82c2b3086533b89", - "reference": "01931139b0f067a4016d5d56e82c2b3086533b89", + "url": "https://api.github.com/repos/sensiolabs/SensioDistributionBundle/zipball/3a900814bd57bf20f9453ae81ff8772bc95d7fff", + "reference": "3a900814bd57bf20f9453ae81ff8772bc95d7fff", "shasum": "" }, "require": { "php": ">=5.3.3", - "sensiolabs/security-checker": "~2.0", + "sensiolabs/security-checker": "~3.0", "symfony/class-loader": "~2.2", "symfony/framework-bundle": "~2.3", "symfony/process": "~2.2" @@ -1395,20 +1401,20 @@ "configuration", "distribution" ], - "time": "2015-05-29 22:35:41" + "time": "2015-08-03 10:07:12" }, { "name": "sensio/framework-extra-bundle", - "version": "v3.0.8", + "version": "v3.0.10", "source": { "type": "git", "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", - "reference": "a30fc18bf147bc25faf6b1d54bf55cfad4b63cba" + "reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/a30fc18bf147bc25faf6b1d54bf55cfad4b63cba", - "reference": "a30fc18bf147bc25faf6b1d54bf55cfad4b63cba", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/18fc2063c4d6569cdca47a39fbac32342eb65f3c", + "reference": "18fc2063c4d6569cdca47a39fbac32342eb65f3c", "shasum": "" }, "require": { @@ -1450,24 +1456,23 @@ "annotations", "controllers" ], - "time": "2015-05-29 18:27:23" + "time": "2015-08-03 11:59:27" }, { "name": "sensiolabs/security-checker", - "version": "v2.0.5", + "version": "v3.0.1", "source": { "type": "git", "url": "https://github.com/sensiolabs/security-checker.git", - "reference": "2c2a71f1c77d9765c12638c4724d9ca23658a810" + "reference": "7735fd97ff7303d9df776b8dbc970f949399abc9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/2c2a71f1c77d9765c12638c4724d9ca23658a810", - "reference": "2c2a71f1c77d9765c12638c4724d9ca23658a810", + "url": "https://api.github.com/repos/sensiolabs/security-checker/zipball/7735fd97ff7303d9df776b8dbc970f949399abc9", + "reference": "7735fd97ff7303d9df776b8dbc970f949399abc9", "shasum": "" }, "require": { - "ext-curl": "*", "symfony/console": "~2.0" }, "bin": [ @@ -1476,7 +1481,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "3.0-dev" } }, "autoload": { @@ -1495,27 +1500,27 @@ } ], "description": "A security checker for your composer.lock", - "time": "2015-05-28 14:22:40" + "time": "2015-08-11 12:11:25" }, { "name": "swiftmailer/swiftmailer", - "version": "v5.4.0", + "version": "v5.4.1", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "31454f258f10329ae7c48763eb898a75c39e0a9f" + "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/31454f258f10329ae7c48763eb898a75c39e0a9f", - "reference": "31454f258f10329ae7c48763eb898a75c39e0a9f", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0697e6aa65c83edf97bb0f23d8763f94e3f11421", + "reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "mockery/mockery": "~0.9.1" + "mockery/mockery": "~0.9.1,<0.9.4" }, "type": "library", "extra": { @@ -1544,39 +1549,45 @@ "description": "Swiftmailer, free feature-rich PHP mailer", "homepage": "http://swiftmailer.org", "keywords": [ + "email", "mail", "mailer" ], - "time": "2015-03-14 06:06:39" + "time": "2015-06-06 14:19:39" }, { "name": "symfony/assetic-bundle", - "version": "v2.6.1", + "version": "v2.7.0", "source": { "type": "git", "url": "https://github.com/symfony/assetic-bundle.git", - "reference": "422b0add2110f0cf9bc7a873a386ea053f4a89f0" + "reference": "3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/assetic-bundle/zipball/422b0add2110f0cf9bc7a873a386ea053f4a89f0", - "reference": "422b0add2110f0cf9bc7a873a386ea053f4a89f0", + "url": "https://api.github.com/repos/symfony/assetic-bundle/zipball/3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5", + "reference": "3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5", "shasum": "" }, "require": { - "kriswallsmith/assetic": "~1.2", + "kriswallsmith/assetic": "~1.3", "php": ">=5.3.0", "symfony/console": "~2.3", "symfony/dependency-injection": "~2.3", "symfony/framework-bundle": "~2.3", "symfony/yaml": "~2.3" }, + "conflict": { + "kriswallsmith/spork": "<=0.2", + "twig/twig": "<1.20" + }, "require-dev": { - "kriswallsmith/spork": "~0.2", + "kriswallsmith/spork": "~0.3", "patchwork/jsqueeze": "~1.0", "symfony/class-loader": "~2.3", "symfony/css-selector": "~2.3", "symfony/dom-crawler": "~2.3", + "symfony/phpunit-bridge": "~2.7", "symfony/twig-bundle": "~2.3" }, "suggest": { @@ -1586,7 +1597,7 @@ "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "2.5-dev" + "dev-master": "2.7-dev" } }, "autoload": { @@ -1612,38 +1623,38 @@ "compression", "minification" ], - "time": "2015-01-27 12:45:16" + "time": "2015-09-01 00:05:29" }, { "name": "symfony/monolog-bundle", - "version": "v2.7.1", + "version": "2.8.1", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bundle.git", - "reference": "9320b6863404c70ebe111e9040dab96f251de7ac" + "reference": "7117b9a145722e3c5768db4585f6ad0643ed5c4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/9320b6863404c70ebe111e9040dab96f251de7ac", - "reference": "9320b6863404c70ebe111e9040dab96f251de7ac", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/7117b9a145722e3c5768db4585f6ad0643ed5c4a", + "reference": "7117b9a145722e3c5768db4585f6ad0643ed5c4a", "shasum": "" }, "require": { "monolog/monolog": "~1.8", "php": ">=5.3.2", - "symfony/config": "~2.3", - "symfony/dependency-injection": "~2.3", - "symfony/http-kernel": "~2.3", - "symfony/monolog-bridge": "~2.3" + "symfony/config": "~2.3|3.*", + "symfony/dependency-injection": "~2.3|3.*", + "symfony/http-kernel": "~2.3|3.*", + "symfony/monolog-bridge": "~2.3|3.*" }, "require-dev": { - "symfony/console": "~2.3", + "symfony/console": "~2.3|3.*", "symfony/yaml": "~2.3" }, "type": "symfony-bundle", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev" + "dev-master": "2.8.x-dev" } }, "autoload": { @@ -1671,7 +1682,7 @@ "log", "logging" ], - "time": "2015-01-04 20:21:17" + "time": "2015-10-02 11:51:59" }, { "name": "symfony/swiftmailer-bundle", @@ -1854,20 +1865,20 @@ }, { "name": "twig/extensions", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig-extensions.git", - "reference": "8cf4b9fe04077bd54fc73f4fde83347040c3b8cd" + "reference": "449e3c8a9ffad7c2479c7864557275a32b037499" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/8cf4b9fe04077bd54fc73f4fde83347040c3b8cd", - "reference": "8cf4b9fe04077bd54fc73f4fde83347040c3b8cd", + "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/449e3c8a9ffad7c2479c7864557275a32b037499", + "reference": "449e3c8a9ffad7c2479c7864557275a32b037499", "shasum": "" }, "require": { - "twig/twig": "~1.12" + "twig/twig": "~1.20|~2.0" }, "require-dev": { "symfony/translation": "~2.3" @@ -1878,7 +1889,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -1902,20 +1913,20 @@ "i18n", "text" ], - "time": "2014-10-30 14:30:03" + "time": "2015-08-22 16:38:35" }, { "name": "twig/twig", - "version": "v1.21.1", + "version": "v1.22.2", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "ca8d3aa90b6a01c82e07909fe815d6b443e75a23" + "reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ca8d3aa90b6a01c82e07909fe815d6b443e75a23", - "reference": "ca8d3aa90b6a01c82e07909fe815d6b443e75a23", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/79249fc8c9ff62e41e217e0c630e2e00bcadda6a", + "reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a", "shasum": "" }, "require": { @@ -1928,7 +1939,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.21-dev" + "dev-master": "1.22-dev" } }, "autoload": { @@ -1963,7 +1974,7 @@ "keywords": [ "templating" ], - "time": "2015-08-26 08:58:31" + "time": "2015-09-22 13:59:32" }, { "name": "zendframework/zend-json", From 3bb54550ebdcdeb215a7d850510a72289e244fac Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 8 Oct 2015 02:11:57 +0300 Subject: [PATCH 31/33] First API method. --- .../Controller/ApiController.php | 45 +++++++++++++++++++ .../Resources/config/api/routing.yml | 5 +++ .../Resources/config/routing.yml | 6 ++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php create mode 100644 src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php new file mode 100644 index 0000000..d136f3c --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php @@ -0,0 +1,45 @@ +getDoctrine()->getRepository('SkobkinPointToolsBundle:SubscriptionEvent')->createQueryBuilder('se'); + $qb + ->select(['se', 'sub']) + ->innerJoin('se.subscriber', 'sub') + ->where($qb->expr()->eq('se.author', ':author')) + ->setParameter('author', $user) + ->setMaxResults(20) + ; + + $data = []; + + /** @var SubscriptionEvent $event */ + foreach ($qb->getQuery()->getResult() as $event) { + $data[] = [ + 'user' => $event->getSubscriber()->getLogin(), + 'action' => $event->getAction(), + 'datetime' => $event->getDate()->format('d.m.Y H:i:s'), + ]; + } + + return new JsonResponse($data); + } +} diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml new file mode 100644 index 0000000..b0035d4 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml @@ -0,0 +1,5 @@ +last_user_events: + path: /user/id/{id}/events + defaults: { _controller: SkobkinPointToolsBundle:Api:lastUserSubscribersById, _format: json } + requirements: + id: \d+ \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml index f153f20..98c91f7 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/routing.yml @@ -19,4 +19,8 @@ users_top: events_last: path: /last - defaults: { _controller: SkobkinPointToolsBundle:Events:last } \ No newline at end of file + defaults: { _controller: SkobkinPointToolsBundle:Events:last } + +skobkin_point_tools: + resource: "@SkobkinPointToolsBundle/Resources/config/api/routing.yml" + prefix: /api/v1 From a429d571a574763aab8fc8f20a60cf703a78c033 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 8 Oct 2015 02:24:36 +0300 Subject: [PATCH 32/33] Route fix. --- .../Bundle/PointToolsBundle/Resources/config/api/routing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml index b0035d4..a094f12 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/api/routing.yml @@ -1,5 +1,5 @@ last_user_events: - path: /user/id/{id}/events + path: /user/id/{id}/events/subscribers defaults: { _controller: SkobkinPointToolsBundle:Api:lastUserSubscribersById, _format: json } requirements: id: \d+ \ No newline at end of file From 2380cdbc1cd776f3178739175c5737dfb0ea5842 Mon Sep 17 00:00:00 2001 From: Alexey Skobkin Date: Thu, 8 Oct 2015 02:40:42 +0300 Subject: [PATCH 33/33] API orderBy() fix. --- src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php b/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php index d136f3c..67b62b9 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Controller/ApiController.php @@ -25,6 +25,7 @@ class ApiController extends Controller ->select(['se', 'sub']) ->innerJoin('se.subscriber', 'sub') ->where($qb->expr()->eq('se.author', ':author')) + ->orderBy('se.date', 'desc') ->setParameter('author', $user) ->setMaxResults(20) ;