diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Comment.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Comment.php index cf4a44f..119794d 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Comment.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Comment.php @@ -8,7 +8,9 @@ use Skobkin\Bundle\PointToolsBundle\Entity\User; /** * Comment * - * @ORM\Table(name="posts.comments") + * @ORM\Table(name="posts.comments", schema="posts", indexes={ + * @ORM\Index(name="idx_comment_created_at", columns={"created_at"}) + * }) * @ORM\Entity */ class Comment @@ -18,7 +20,6 @@ class Comment * * @ORM\Column(name="id", type="integer") * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") */ private $id; @@ -32,27 +33,28 @@ class Comment /** * @var \DateTime * - * @ORM\Column(name="createdAt", type="datetime") + * @ORM\Column(name="created_at", type="datetime") */ private $createdAt; /** * @var boolean * - * @ORM\Column(name="isRec", type="boolean") + * @ORM\Column(name="is_rec", type="boolean") */ - private $isRec; + private $rec; /** - * @var int + * @var bool * - * @ORM\Column(name="number", type="integer") + * @ORM\Column(name="is_deleted", type="boolean") */ - private $number; + private $deleted = false; /** * @var Post * + * @ORM\Id * @ORM\ManyToOne(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Post") * @ORM\JoinColumn(name="post_id") */ @@ -67,13 +69,42 @@ class Comment private $author; /** - * @var Comment + * @var Comment|null * * @ORM\ManyToOne(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Comment") * @ORM\JoinColumn(name="to_comment_id", nullable=true) */ private $toComment; + /** + * @param int $id + * @param Post $post + * @param User $author + * @param Comment|null $toComment + * @param string $text + * @param \DateTime $createdAt + * @param bool $isRec + */ + public function __construct($id, Post $post, User $author, Comment $toComment = null, $text, \DateTime $createdAt, $isRec) + { + if (!is_numeric($id)) { + throw new \InvalidArgumentException('$id must be an integer'); + } + if (!is_bool($isRec)) { + throw new \InvalidArgumentException('$isRec must be boolean'); + } + if (!is_string($text)) { + throw new \InvalidArgumentException('$text must be a string'); + } + + $this->id = (int)$id; + $this->post = $post; + $this->author = $author; + $this->toComment = $toComment; + $this->text = $text; + $this->createdAt = $createdAt; + $this->rec = $isRec; + } /** * Get id @@ -134,12 +165,12 @@ class Comment /** * Set isRec * - * @param boolean $isRec + * @param boolean $rec * @return Comment */ - public function setIsRec($isRec) + public function setRec($rec) { - $this->isRec = $isRec; + $this->rec = $rec; return $this; } @@ -151,26 +182,17 @@ class Comment */ public function isRec() { - return $this->isRec; + return $this->rec; } /** - * @return int + * Get isRec + * + * @return boolean */ - public function getNumber() + public function getRec() { - return $this->number; - } - - /** - * @param int $number - * @return Comment - */ - public function setNumber($number) - { - $this->number = $number; - - return $this; + return $this->rec; } /** @@ -230,5 +252,36 @@ class Comment return $this; } + /** + * Set deleted + * + * @param boolean $deleted + * @return Comment + */ + public function setDeleted($deleted) + { + $this->deleted = $deleted; + return $this; + } + + /** + * Get deleted + * + * @return boolean + */ + public function getDeleted() + { + return $this->deleted; + } + + /** + * Get deleted + * + * @return boolean + */ + public function isDeleted() + { + return $this->deleted; + } } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Post.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Post.php index 28ee669..f472dbd 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Post.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Post.php @@ -3,14 +3,15 @@ namespace Skobkin\Bundle\PointToolsBundle\Entity\Blogs; use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Skobkin\Bundle\PointToolsBundle\Entity\User; /** * Post * - * @ORM\Table(name="posts.posts") + * @ORM\Table(name="posts.posts", schema="posts", indexes={ + * @ORM\Index(name="idx_post_created_at", columns={"created_at"}) + * }) * @ORM\Entity */ class Post @@ -23,13 +24,6 @@ class Post */ private $id; - /** - * @var integer - * - * @ORM\Column(name="uid", type="integer") - */ - private $uid; - /** * @var string * @@ -40,7 +34,7 @@ class Post /** * @var \DateTime * - * @ORM\Column(name="createdAt", type="datetime") + * @ORM\Column(name="created_at", type="datetime") */ private $createdAt; @@ -51,6 +45,13 @@ class Post */ private $type; + /** + * @var bool + * + * @ORM\Column(name="is_deleted", type="boolean") + */ + private $deleted = false; + /** * @var User * @@ -60,7 +61,7 @@ class Post private $author; /** - * @var Tag[]|Collection + * @var Tag[]|ArrayCollection * * @ORM\ManyToMany(targetEntity="Skobkin\Bundle\PointToolsBundle\Entity\Blogs\Tag", fetch="EXTRA_LAZY") * @ORM\JoinTable(name="posts.posts_tags", @@ -71,13 +72,19 @@ class Post private $tags; /** - * @var Comment[]|Collection + * @var Comment[]|ArrayCollection */ private $comments; - public function __construct() + public function __construct($id, $type, $text, \DateTime $createdAt, User $author = null) { + $this->id = $id; + $this->type = $type; + $this->createdAt = $createdAt; + $this->text = $text; + $this->author = $author; + $this->tags = new ArrayCollection(); $this->comments = new ArrayCollection(); } @@ -92,29 +99,6 @@ class Post return $this->id; } - /** - * Set uid - * - * @param integer $uid - * @return Post - */ - public function setUid($uid) - { - $this->uid = $uid; - - return $this; - } - - /** - * Get uid - * - * @return integer - */ - public function getUid() - { - return $this->uid; - } - /** * Set text * @@ -202,29 +186,15 @@ class Post return $this; } - - /** - * Set id - * - * @param string $id - * @return Post - */ - public function setId($id) - { - $this->id = $id; - - return $this; - } - /** * Add tags * - * @param Tag $tags + * @param Tag $tag * @return Post */ - public function addTag(Tag $tags) + public function addTag(Tag $tag) { - $this->tags[] = $tags; + $this->tags[] = $tag; return $this; } @@ -232,20 +202,53 @@ class Post /** * Remove tags * - * @param Tag $tags + * @param Tag $tag */ - public function removeTag(Tag $tags) + public function removeTag(Tag $tag) { - $this->tags->removeElement($tags); + $this->tags->removeElement($tag); } /** * Get tags * - * @return Collection + * @return ArrayCollection */ public function getTags() { return $this->tags; } + + /** + * Set deleted + * + * @param boolean $deleted + * @return Post + */ + public function setDeleted($deleted) + { + $this->deleted = $deleted; + + return $this; + } + + /** + * Get deleted + * + * @return boolean + */ + public function getDeleted() + { + return $this->deleted; + } + + /** + * Get deleted + * + * @return boolean + */ + public function isDeleted() + { + return $this->deleted; + } } diff --git a/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Tag.php b/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Tag.php index 7a9d5ac..8198974 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Tag.php +++ b/src/Skobkin/Bundle/PointToolsBundle/Entity/Blogs/Tag.php @@ -7,7 +7,9 @@ use Doctrine\ORM\Mapping as ORM; /** * Tag * - * @ORM\Table(name="posts.tags") + * @ORM\Table(name="posts.tags", schema="posts", indexes={ + * @ORM\Index(name="idx_tag_text", columns={"text"}) + * }) * @ORM\Entity */ class Tag @@ -24,11 +26,19 @@ class Tag /** * @var string * - * @ORM\Column(name="text", type="string", length=128, unique=true) + * @ORM\Column(name="text", type="text", unique=true) */ private $text; + /** + * @param string $text + */ + public function __construct($text) + { + $this->text = $text; + } + /** * Get id * diff --git a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/services.yml b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/services.yml index f81d137..02c4b66 100644 --- a/src/Skobkin/Bundle/PointToolsBundle/Resources/config/services.yml +++ b/src/Skobkin/Bundle/PointToolsBundle/Resources/config/services.yml @@ -13,6 +13,30 @@ services: - "%point_api_base_url%" - @doctrine.orm.entity_manager + skobkin_point_tools.api_post: + class: Skobkin\Bundle\PointToolsBundle\Service\PostApi + arguments: + - @skobkin_point_tools.http_client + - "%point_use_https%" + - "%point_api_base_url%" + - @doctrine.orm.entity_manager + skobkin_point_tools.subscriptions_manager: class: Skobkin\Bundle\PointToolsBundle\Service\SubscriptionsManager - arguments: [ @doctrine.orm.entity_manager ] \ No newline at end of file + arguments: [ @doctrine.orm.entity_manager ] + + skobkin__point_tools.service_factory.user_factory: + class: Skobkin\Bundle\PointToolsBundle\Service\Factory\UserFactory + arguments: [ @doctrine.orm.entity_manager ] + + skobkin__point_tools.service_factory.comment_factory: + class: Skobkin\Bundle\PointToolsBundle\Service\Factory\CommentFactory + arguments: [ @doctrine.orm.entity_manager, @skobkin__point_tools.service_factory.user_factory ] + + skobkin__point_tools.service_factory.tag_factory: + class: Skobkin\Bundle\PointToolsBundle\Service\Factory\TagFactory + arguments: [ @doctrine.orm.entity_manager ] + + skobkin__point_tools.service_factory.post_factory: + class: Skobkin\Bundle\PointToolsBundle\Service\Factory\PostFactory + arguments: [ @doctrine.orm.entity_manager, @skobkin__point_tools.service_factory.user_factory, @skobkin__point_tools.service_factory.comment_factory, @skobkin__point_tools.service_factory.tag_factory ] diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/CommentFactory.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/CommentFactory.php new file mode 100644 index 0000000..be52247 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/CommentFactory.php @@ -0,0 +1,124 @@ +em = $em; + $this->userFactory = $userFactory; + $this->commentRepository = $em->getRepository('SkobkinPointToolsBundle:Blogs\Comment'); + $this->postRepository = $em->getRepository('SkobkinPointToolsBundle:Blogs\Post'); + } + + /** + * @param array $data + * + * @return Comment + * @throws ApiException + * @throws InvalidResponseException + */ + public function createFromArray(array $data) + { + $this->validateData($data); + + if (null === ($comment = $this->commentRepository->find(['post' => $data['post_id'], 'id' => $data['id']]))) { + // @fixme rare non-existing post bug + $post = $this->postRepository->find($data['post_id']); + $author = $this->userFactory->createFromArray($data['author']); + if (null !== $data['to_comment_id']) { + $toComment = $this->commentRepository->find(['post' => $data['post_id'], 'id' => $data['to_comment_id']]); + } else { + $toComment = null; + } + $createdAt = new \DateTime($data['created']); + + $comment = new Comment($data['id'], $post, $author, $toComment, $data['text'], $createdAt, $data['is_rec']); + + $this->em->persist($comment); + } + + try { + $this->em->flush($comment); + } catch (\Exception $e) { + throw new ApiException(sprintf('Error while flushing changes for #%s/%d: %s', $data['post_id'], $data['id'], $e->getMessage()), 0, $e); + } + + return $comment; + } + + /** + * @param array $data + * + * @return Comment[] + * @throws ApiException + */ + public function createFromListArray(array $data) + { + $comments = []; + + foreach ($data as $commentData) { + $comments[] = $this->createFromArray($commentData); + } + + return $comments; + } + + /** + * @param array $data + * + * @throws InvalidResponseException + */ + private function validateData(array $data) + { + if (!array_key_exists('author', $data)) { + throw new InvalidResponseException('Comment author data not found in API response'); + } + + // Post + if (!( + array_key_exists('id', $data) && + array_key_exists('is_rec', $data) && + array_key_exists('to_comment_id', $data) && + array_key_exists('post_id', $data) && + array_key_exists('text', $data) && + array_key_exists('created', $data) + )) { + throw new InvalidResponseException('Comment data not found in API response'); + } + } +} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/PostFactory.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/PostFactory.php new file mode 100644 index 0000000..6aa9447 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/PostFactory.php @@ -0,0 +1,113 @@ +userFactory = $userFactory; + $this->commentFactory = $commentFactory; + $this->tagFactory = $tagFactory; + $this->em = $em; + $this->postRepository = $em->getRepository('SkobkinPointToolsBundle:Blogs\Post'); + } + + public function createFromArray(array $data) + { + $this->validateData($data); + + if (null === ($post = $this->postRepository->find($data['post']['id']))) { + $createdAt = new \DateTime($data['post']['created']); + $author = $this->userFactory->createFromArray($data['post']['author']); + + $post = new Post($data['post']['id'], $data['post']['type'], $data['post']['text'], $createdAt, $author); + $this->em->persist($post); + } + + $post->setText($data['post']['text']); + + // Tags + $tags = $this->tagFactory->createFromListArray($data['post']['tags']); + + // Removing deleted tags + foreach ($post->getTags() as $tag) { + if (false === in_array($tag, $tags, true)) { + $post->removeTag($tag); + } + } + + // Adding new tags + foreach ($tags as $tag) { + if (!$post->getTags()->contains($tag)) { + $post->addTag($tag); + } + } + + // Comments + $comments = $this->commentFactory->createFromListArray($data['comments']); + + // Marking removed comments + + return $post; + } + + private function validateData(array $data) + { + if (!array_key_exists('post', $data)) { + throw new InvalidResponseException('Post data not found in API response'); + } + + if (!array_key_exists('comments', $data)) { + throw new InvalidResponseException('Comments data not found in API response'); + } + + if (!( + array_key_exists('id', $data['post']) && + array_key_exists('type', $data['post']) && + array_key_exists('text', $data['post']) && + array_key_exists('tags', $data['post']) && + array_key_exists('author', $data['post']) + )) { + throw new InvalidResponseException('Post content not found in API response'); + } + } +} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/TagFactory.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/TagFactory.php new file mode 100644 index 0000000..ce49a24 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/TagFactory.php @@ -0,0 +1,95 @@ +em = $em; + $this->tagRepository = $em->getRepository('SkobkinPointToolsBundle:Blogs\Tag'); + } + + /** + * @param $data + * + * @return Tag + * @throws ApiException + * @throws InvalidResponseException + * @throws \Doctrine\ORM\NonUniqueResultException + */ + public function createFromArray($data) + { + $this->validateData($data); + + $qb = $this->tagRepository->createQueryBuilder('t'); + $qb + ->select() + ->where($qb->expr()->eq('lower(t.text)', 'lower(:text)')) + ->setParameter('text', $data) + ; + + if (null === ($tag = $qb->getQuery()->getOneOrNullResult())) { + $tag = new Tag($data); + $this->em->persist($tag); + } + + try { + $this->em->flush($tag); + } catch (\Exception $e) { + throw new ApiException(sprintf('Error while flushing changes for [%d] %s: %s', $tag->getId(), $tag->getText(), $e->getMessage()), 0, $e); + } + + return $tag; + } + + /** + * @param array $data + * + * @return Tag[] + * @throws ApiException + */ + public function createFromListArray(array $data) + { + $tags = []; + + foreach ($data as $text) { + $tags[] = $this->createFromArray($text); + } + + return $tags; + } + + /** + * @param $data + * + * @throws InvalidResponseException + */ + private function validateData($data) + { + if (!is_string($data)) { + throw new InvalidResponseException('Tag data must be a string'); + } + } +} \ No newline at end of file diff --git a/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/UserFactory.php b/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/UserFactory.php new file mode 100644 index 0000000..ae4b2e6 --- /dev/null +++ b/src/Skobkin/Bundle/PointToolsBundle/Service/Factory/UserFactory.php @@ -0,0 +1,80 @@ +em = $em; + $this->userRepository = $em->getRepository('SkobkinPointToolsBundle:User'); + } + + /** + * @param array $data + * + * @return User + * @throws ApiException + * @throws InvalidResponseException + */ + public function createFromArray(array $data) + { + $this->validateData($data); + + // @todo Return ID existance check when @ap-Codkelden will fix this API behaviour + /** @var User $user */ + if (null === ($user = $this->userRepository->find($data['id']))) { + // Creating new user + $user = new User($data['id']); + $this->em->persist($user); + } + + // Updating data + $user + ->setLogin($data['login']) + ->setName($data['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; + } + + /** + * @param array $data + * + * @throws InvalidResponseException + */ + private function validateData(array $data) + { + if (!(array_key_exists('id', $data) && array_key_exists('login', $data) && array_key_exists('name', $data))) { + throw new InvalidResponseException('Invalid user data'); + } + } +} \ No newline at end of file