diff --git a/src/DataFixtures/FixturesTrait.php b/src/DataFixtures/AppFixtures.php similarity index 51% rename from src/DataFixtures/FixturesTrait.php rename to src/DataFixtures/AppFixtures.php index 334aed0dc..00de4cc5b 100644 --- a/src/DataFixtures/FixturesTrait.php +++ b/src/DataFixtures/AppFixtures.php @@ -11,49 +11,131 @@ namespace App\DataFixtures; -/** - * Helper class to create fixtures contents. - */ -trait FixturesTrait +use App\Entity\Comment; +use App\Entity\Post; +use App\Entity\Tag; +use App\Entity\User; +use App\Utils\Slugger; +use Doctrine\Bundle\FixturesBundle\Fixture; +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; + +class AppFixtures extends Fixture { - private function getPostContent(): string + private $passwordEncoder; + + public function __construct(UserPasswordEncoderInterface $passwordEncoder) { - return <<<'MARKDOWN' -Lorem ipsum dolor sit amet consectetur adipisicing elit, sed do eiusmod tempor -incididunt ut labore et **dolore magna aliqua**: Duis aute irure dolor in -reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. -Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia -deserunt mollit anim id est laborum. + $this->passwordEncoder = $passwordEncoder; + } - * Ut enim ad minim veniam - * Quis nostrud exercitation *ullamco laboris* - * Nisi ut aliquip ex ea commodo consequat + public function load(ObjectManager $manager) + { + $this->loadUsers($manager); + $this->loadTags($manager); + $this->loadPosts($manager); + } -Praesent id fermentum lorem. Ut est lorem, fringilla at accumsan nec, euismod at -nunc. Aenean mattis sollicitudin mattis. Nullam pulvinar vestibulum bibendum. -Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos -himenaeos. Fusce nulla purus, gravida ac interdum ut, blandit eget ex. Duis a -luctus dolor. + private function loadUsers(ObjectManager $manager) + { + foreach ($this->getUserData() as [$fullname, $username, $password, $email, $roles]) { + $user = new User(); + $user->setFullName($fullname); + $user->setUsername($username); + $user->setPassword($this->passwordEncoder->encodePassword($user, $password)); + $user->setEmail($email); + $user->setRoles($roles); + + $manager->persist($user); + $this->addReference($username, $user); + } -Integer auctor massa maximus nulla scelerisque accumsan. *Aliquam ac malesuada* -ex. Pellentesque tortor magna, vulputate eu vulputate ut, venenatis ac lectus. -Praesent ut lacinia sem. Mauris a lectus eget felis mollis feugiat. Quisque -efficitur, mi ut semper pulvinar, urna urna blandit massa, eget tincidunt augue -nulla vitae est. + $manager->flush(); + } -Ut posuere aliquet tincidunt. Aliquam erat volutpat. **Class aptent taciti** -sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi -arcu orci, gravida eget aliquam eu, suscipit et ante. Morbi vulputate metus vel -ipsum finibus, ut dapibus massa feugiat. Vestibulum vel lobortis libero. Sed -tincidunt tellus et viverra scelerisque. Pellentesque tincidunt cursus felis. -Sed in egestas erat. + private function loadTags(ObjectManager $manager) + { + foreach ($this->getTagData() as $index => $name) { + $tag = new Tag(); + $tag->setName($name); -Aliquam pulvinar interdum massa, vel ullamcorper ante consectetur eu. Vestibulum -lacinia ac enim vel placerat. Integer pulvinar magna nec dui malesuada, nec -congue nisl dictum. Donec mollis nisl tortor, at congue erat consequat a. Nam -tempus elit porta, blandit elit vel, viverra lorem. Sed sit amet tellus -tincidunt, faucibus nisl in, aliquet libero. -MARKDOWN; + $manager->persist($tag); + $this->addReference('tag-'.$name, $tag); + } + + $manager->flush(); + } + + private function loadPosts(ObjectManager $manager) + { + foreach ($this->getPostData() as [$title, $slug, $summary, $content, $publishedAt, $author, $tags]) { + $post = new Post(); + $post->setTitle($title); + $post->setSlug($slug); + $post->setSummary($summary); + $post->setContent($content); + $post->setPublishedAt($publishedAt); + $post->setAuthor($author); + $post->addTag(...$tags); + + foreach (range(1, 5) as $i) { + $comment = new Comment(); + $comment->setAuthor($this->getReference('john_user')); + $comment->setContent($this->getRandomText(random_int(255, 512))); + $comment->setPublishedAt(new \DateTime('now + '.($i).'seconds')); + + $post->addComment($comment); + } + + $manager->persist($post); + } + + $manager->flush(); + } + + private function getUserData(): array + { + return [ + // $userData = [$fullname, $username, $password, $email, $roles]; + ['Jane Doe', 'jane_admin', 'kitten', 'jane_admin@symfony.com', ['ROLE_ADMIN']], + ['Tom Doe', 'tom_admin', 'kitten', 'tom_admin@symfony.com', ['ROLE_ADMIN']], + ['John Doe', 'john_user', 'kitten', 'john_user@symfony.com', ['ROLE_USER']], + ]; + } + + private function getTagData(): array + { + return [ + 'lorem', + 'ipsum', + 'consectetur', + 'adipiscing', + 'incididunt', + 'labore', + 'voluptate', + 'dolore', + 'pariatur', + ]; + } + + private function getPostData() + { + $posts = []; + foreach ($this->getPhrases() as $i => $title) { + // $postData = [$title, $slug, $summary, $content, $publishedAt, $author, $tags, $comments]; + $posts[] = [ + $title, + Slugger::slugify($title), + $this->getRandomText(), + $this->getPostContent(), + new \DateTime('now - '.$i.'days'), + // Ensure that the first post is written by Jane Doe to simplify tests + $this->getReference(['jane_admin', 'tom_admin'][0 === $i ? 0 : random_int(0, 1)]), + $this->getRandomTags(), + ]; + } + + return $posts; } private function getPhrases(): array @@ -92,55 +174,64 @@ private function getPhrases(): array ]; } - private function getTagNames(): array - { - return [ - 'lorem', - 'ipsum', - 'consectetur', - 'adipiscing', - 'incididunt', - 'labore', - 'voluptate', - 'dolore', - 'pariatur', - ]; - } - - private function getRandomPostTitles(): array + private function getRandomText(int $maxLength = 255): string { $phrases = $this->getPhrases(); - - // this ensures that the first title is always 'Lorem Ipsum...' - $loremIpsumPhrase = array_shift($phrases); shuffle($phrases); - array_unshift($phrases, $loremIpsumPhrase); - return $phrases; + while (mb_strlen($text = implode('. ', $phrases).'.') > $maxLength) { + array_pop($phrases); + } + + return $text; } - private function getRandomPostSummary(int $maxLength = 255): string + private function getPostContent(): string { - $phrases = $this->getPhrases(); + return <<<'MARKDOWN' +Lorem ipsum dolor sit amet consectetur adipisicing elit, sed do eiusmod tempor +incididunt ut labore et **dolore magna aliqua**: Duis aute irure dolor in +reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia +deserunt mollit anim id est laborum. - $numPhrases = random_int(6, 12); - shuffle($phrases); - $phrases = array_slice($phrases, 0, $numPhrases - 1); + * Ut enim ad minim veniam + * Quis nostrud exercitation *ullamco laboris* + * Nisi ut aliquip ex ea commodo consequat - while (mb_strlen($summary = implode('. ', $phrases).'.') > $maxLength) { - array_pop($phrases); - } +Praesent id fermentum lorem. Ut est lorem, fringilla at accumsan nec, euismod at +nunc. Aenean mattis sollicitudin mattis. Nullam pulvinar vestibulum bibendum. +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos +himenaeos. Fusce nulla purus, gravida ac interdum ut, blandit eget ex. Duis a +luctus dolor. + +Integer auctor massa maximus nulla scelerisque accumsan. *Aliquam ac malesuada* +ex. Pellentesque tortor magna, vulputate eu vulputate ut, venenatis ac lectus. +Praesent ut lacinia sem. Mauris a lectus eget felis mollis feugiat. Quisque +efficitur, mi ut semper pulvinar, urna urna blandit massa, eget tincidunt augue +nulla vitae est. - return $summary; +Ut posuere aliquet tincidunt. Aliquam erat volutpat. **Class aptent taciti** +sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi +arcu orci, gravida eget aliquam eu, suscipit et ante. Morbi vulputate metus vel +ipsum finibus, ut dapibus massa feugiat. Vestibulum vel lobortis libero. Sed +tincidunt tellus et viverra scelerisque. Pellentesque tincidunt cursus felis. +Sed in egestas erat. + +Aliquam pulvinar interdum massa, vel ullamcorper ante consectetur eu. Vestibulum +lacinia ac enim vel placerat. Integer pulvinar magna nec dui malesuada, nec +congue nisl dictum. Donec mollis nisl tortor, at congue erat consequat a. Nam +tempus elit porta, blandit elit vel, viverra lorem. Sed sit amet tellus +tincidunt, faucibus nisl in, aliquet libero. +MARKDOWN; } - private function getRandomCommentContent(): string + private function getRandomTags(): array { - $phrases = $this->getPhrases(); - - $numPhrases = random_int(2, 15); - shuffle($phrases); + $tagNames = $this->getTagData(); + shuffle($tagNames); + $selectedTags = array_slice($tagNames, 0, random_int(2, 4)); - return implode(' ', array_slice($phrases, 0, $numPhrases - 1)); + return array_map(function ($tagName) { return $this->getReference('tag-'.$tagName); }, $selectedTags); } } diff --git a/src/DataFixtures/PostFixtures.php b/src/DataFixtures/PostFixtures.php deleted file mode 100644 index ea5c8728c..000000000 --- a/src/DataFixtures/PostFixtures.php +++ /dev/null @@ -1,117 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace App\DataFixtures; - -use App\Entity\Comment; -use App\Entity\Post; -use App\Entity\User; -use App\Utils\Slugger; -use Doctrine\Common\DataFixtures\AbstractFixture; -use Doctrine\Common\DataFixtures\DependentFixtureInterface; -use Doctrine\Common\Persistence\ObjectManager; - -/** - * Defines the sample blog posts to load in the database before running the unit - * and functional tests. Execute this command to load the data. - * - * $ php bin/console doctrine:fixtures:load - * - * See https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html - * - * @author Ryan Weaver - * @author Javier Eguiluz - * @author Yonel Ceruto - */ -class PostFixtures extends AbstractFixture implements DependentFixtureInterface -{ - use FixturesTrait; - - /** - * {@inheritdoc} - */ - public function load(ObjectManager $manager): void - { - foreach ($this->getRandomPostTitles() as $i => $title) { - $post = new Post(); - - $post->setTitle($title); - $post->setSummary($this->getRandomPostSummary()); - $post->setSlug(Slugger::slugify($post->getTitle())); - $post->setContent($this->getPostContent()); - $post->setPublishedAt(new \DateTime('now - '.$i.'days')); - - // Ensure that the first post is written by Jane Doe to simplify tests - // "References" are the way to share objects between fixtures defined - // in different files. This reference has been added in the UserFixtures - // file and it contains an instance of the User entity. - $post->setAuthor(0 === $i ? $this->getReference('jane-admin') : $this->getRandomUser()); - - // for aesthetic reasons, the first blog post always has 2 tags - foreach ($this->getRandomTags($i > 0 ? random_int(0, 3) : 2) as $tag) { - $post->addTag($tag); - } - - foreach (range(1, 5) as $j) { - $comment = new Comment(); - - $comment->setAuthor($this->getReference('john-user')); - $comment->setPublishedAt(new \DateTime('now + '.($i + $j).'seconds')); - $comment->setContent($this->getRandomCommentContent()); - - $post->addComment($comment); - - $manager->persist($comment); - } - - $manager->persist($post); - } - - $manager->flush(); - } - - /** - * Instead of defining the exact order in which the fixtures files must be loaded, - * this method defines which other fixtures this file depends on. Then, Doctrine - * will figure out the best order to fit all the dependencies. - */ - public function getDependencies(): array - { - return [ - TagFixtures::class, - UserFixtures::class, - ]; - } - - private function getRandomUser(): User - { - $admins = ['jane-admin', 'tom-admin']; - $index = array_rand($admins); - - return $this->getReference($admins[$index]); - } - - private function getRandomTags(int $numTags = 0): array - { - $tags = []; - - if (0 === $numTags) { - return $tags; - } - - $indexes = (array) array_rand($this->getTagNames(), $numTags); - foreach ($indexes as $index) { - $tags[] = $this->getReference('tag-'.$index); - } - - return $tags; - } -} diff --git a/src/DataFixtures/TagFixtures.php b/src/DataFixtures/TagFixtures.php deleted file mode 100644 index 9dccddb1d..000000000 --- a/src/DataFixtures/TagFixtures.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace App\DataFixtures; - -use App\Entity\Tag; -use Doctrine\Common\DataFixtures\AbstractFixture; -use Doctrine\Common\Persistence\ObjectManager; - -/** - * Defines the sample blog tags to load in the database before running the unit - * and functional tests. Execute this command to load the data. - * - * $ php bin/console doctrine:fixtures:load - * - * See https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html - * - * @author Yonel Ceruto - */ -class TagFixtures extends AbstractFixture -{ - use FixturesTrait; - - /** - * {@inheritdoc} - */ - public function load(ObjectManager $manager): void - { - foreach ($this->getTagNames() as $index => $name) { - $tag = new Tag(); - $tag->setName($name); - - $manager->persist($tag); - $this->addReference('tag-'.$index, $tag); - } - - $manager->flush(); - } -} diff --git a/src/DataFixtures/UserFixtures.php b/src/DataFixtures/UserFixtures.php deleted file mode 100644 index d443a061f..000000000 --- a/src/DataFixtures/UserFixtures.php +++ /dev/null @@ -1,77 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace App\DataFixtures; - -use App\Entity\User; -use Doctrine\Common\DataFixtures\AbstractFixture; -use Doctrine\Common\Persistence\ObjectManager; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; - -/** - * Defines the sample users to load in the database before running the unit and - * functional tests. Execute this command to load the data. - * - * $ php bin/console doctrine:fixtures:load - * - * See https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html - * - * @author Ryan Weaver - * @author Javier Eguiluz - * @author Yonel Ceruto - */ -class UserFixtures extends AbstractFixture implements ContainerAwareInterface -{ - use ContainerAwareTrait; - - /** - * {@inheritdoc} - */ - public function load(ObjectManager $manager): void - { - $passwordEncoder = $this->container->get('security.password_encoder'); - - $janeAdmin = new User(); - $janeAdmin->setFullName('Jane Doe'); - $janeAdmin->setUsername('jane_admin'); - $janeAdmin->setEmail('jane_admin@symfony.com'); - $janeAdmin->setRoles(['ROLE_ADMIN']); - $encodedPassword = $passwordEncoder->encodePassword($janeAdmin, 'kitten'); - $janeAdmin->setPassword($encodedPassword); - $manager->persist($janeAdmin); - // In case if fixture objects have relations to other fixtures, adds a reference - // to that object by name and later reference it to form a relation. - // See https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#sharing-objects-between-fixtures - $this->addReference('jane-admin', $janeAdmin); - - $tomAdmin = new User(); - $tomAdmin->setFullName('Tom Doe'); - $tomAdmin->setUsername('tom_admin'); - $tomAdmin->setEmail('tom_admin@symfony.com'); - $tomAdmin->setRoles(['ROLE_ADMIN']); - $encodedPassword = $passwordEncoder->encodePassword($tomAdmin, 'kitten'); - $tomAdmin->setPassword($encodedPassword); - $manager->persist($tomAdmin); - $this->addReference('tom-admin', $tomAdmin); - - $johnUser = new User(); - $johnUser->setFullName('John Doe'); - $johnUser->setUsername('john_user'); - $johnUser->setEmail('john_user@symfony.com'); - $encodedPassword = $passwordEncoder->encodePassword($johnUser, 'kitten'); - $johnUser->setPassword($encodedPassword); - $manager->persist($johnUser); - $this->addReference('john-user', $johnUser); - - $manager->flush(); - } -} diff --git a/src/Entity/Post.php b/src/Entity/Post.php index 4689691be..8bf6853a1 100644 --- a/src/Entity/Post.php +++ b/src/Entity/Post.php @@ -104,7 +104,8 @@ class Post * @ORM\OneToMany( * targetEntity="Comment", * mappedBy="post", - * orphanRemoval=true + * orphanRemoval=true, + * cascade={"persist"} * ) * @ORM\OrderBy({"publishedAt": "DESC"}) */ @@ -211,10 +212,12 @@ public function setSummary(string $summary): void $this->summary = $summary; } - public function addTag(Tag $tag): void + public function addTag(Tag ...$tags): void { - if (!$this->tags->contains($tag)) { - $this->tags->add($tag); + foreach ($tags as $tag) { + if (!$this->tags->contains($tag)) { + $this->tags->add($tag); + } } } diff --git a/tests/Controller/Admin/BlogControllerTest.php b/tests/Controller/Admin/BlogControllerTest.php index 7dee0bbae..23235d463 100644 --- a/tests/Controller/Admin/BlogControllerTest.php +++ b/tests/Controller/Admin/BlogControllerTest.php @@ -11,7 +11,6 @@ namespace App\Tests\Controller\Admin; -use App\DataFixtures\FixturesTrait; use App\Entity\Post; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\Response; @@ -33,8 +32,6 @@ */ class BlogControllerTest extends WebTestCase { - use FixturesTrait; - /** * @dataProvider getUrlsForRegularUsers */ @@ -83,8 +80,8 @@ public function testAdminBackendHomePage() public function testAdminNewPost() { $postTitle = 'Blog Post Title '.mt_rand(); - $postSummary = $this->getRandomPostSummary(); - $postContent = $this->getPostContent(); + $postSummary = $this->generateRandomString(255); + $postContent = $this->generateRandomString(1024); $client = static::createClient([], [ 'PHP_AUTH_USER' => 'jane_admin', @@ -166,4 +163,11 @@ public function testAdminDeletePost() $post = $client->getContainer()->get('doctrine')->getRepository(Post::class)->find(1); $this->assertNull($post); } + + private function generateRandomString(int $length): string + { + $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + + return mb_substr(str_shuffle(str_repeat($chars, ceil($length / mb_strlen($chars)))), 1, $length); + } } diff --git a/var/data/blog.sqlite b/var/data/blog.sqlite index 1bdf92e07..1ff16420e 100644 Binary files a/var/data/blog.sqlite and b/var/data/blog.sqlite differ