From a9be58302c45868134212ccdf22c5389423ef0e9 Mon Sep 17 00:00:00 2001 From: "art.dambrine" Date: Thu, 1 Apr 2021 22:33:37 +0200 Subject: [PATCH] implementing image upload with vich/uploader-bundle --- .gitignore | 1 + composer.json | 3 +- composer.lock | 163 ++++++++++++++++++++++++++- config/bundles.php | 1 + config/packages/vich_uploader.yaml | 12 ++ migrations/Version20210401194203.php | 31 +++++ migrations/Version20210401194958.php | 31 +++++ migrations/Version20210401200205.php | 31 +++++ migrations/Version20210401200906.php | 31 +++++ migrations/Version20210401202700.php | 31 +++++ migrations/Version20210401202758.php | 31 +++++ src/Controller/PokemonController.php | 15 +++ src/Entity/Pokemon.php | 78 +++++++++++++ src/Form/PokemonType.php | 9 ++ symfony.lock | 15 +++ templates/pokemon/show.html.twig | 4 + 16 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 config/packages/vich_uploader.yaml create mode 100644 migrations/Version20210401194203.php create mode 100644 migrations/Version20210401194958.php create mode 100644 migrations/Version20210401200205.php create mode 100644 migrations/Version20210401200906.php create mode 100644 migrations/Version20210401202700.php create mode 100644 migrations/Version20210401202758.php diff --git a/.gitignore b/.gitignore index 6f33dab..3fb3c17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .idea/ +/public/images/pokemons ###> symfony/framework-bundle ### /.env.local /.env.local.php diff --git a/composer.json b/composer.json index 782d4c3..6a0ed31 100644 --- a/composer.json +++ b/composer.json @@ -43,7 +43,8 @@ "symfony/web-link": "5.2.*", "symfony/yaml": "5.2.*", "twig/extra-bundle": "^2.12|^3.0", - "twig/twig": "^2.12|^3.0" + "twig/twig": "^2.12|^3.0", + "vich/uploader-bundle": "^1.17" }, "require-dev": { "symfony/browser-kit": "^5.2", diff --git a/composer.lock b/composer.lock index 38b4a07..138c361 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7f19c1fbf5a162c6ac24cf3f55978592", + "content-hash": "4193f9739f8bbbcc296b1562066343b1", "packages": [ { "name": "api-platform/core", @@ -1683,6 +1683,66 @@ ], "time": "2021-01-14T21:52:44+00:00" }, + { + "name": "jms/metadata", + "version": "2.5.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/metadata.git", + "reference": "b5c52549807b2d855b3d7e36ec164c00eb547338" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/b5c52549807b2d855b3d7e36ec164c00eb547338", + "reference": "b5c52549807b2d855b3d7e36ec164c00eb547338", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "require-dev": { + "doctrine/cache": "^1.0", + "doctrine/coding-standard": "^8.0", + "mikey179/vfsstream": "^1.6.7", + "phpunit/phpunit": "^8.5|^9.0", + "psr/container": "^1.0", + "symfony/cache": "^3.1|^4.0|^5.0", + "symfony/dependency-injection": "^3.1|^4.0|^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Metadata\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "Class/method/property metadata management in PHP", + "keywords": [ + "annotations", + "metadata", + "xml", + "yaml" + ], + "time": "2021-03-07T19:20:09+00:00" + }, { "name": "knplabs/knp-components", "version": "v3.0.2", @@ -7506,6 +7566,107 @@ ], "time": "2021-02-08T09:54:36+00:00" }, + { + "name": "vich/uploader-bundle", + "version": "1.17.0", + "source": { + "type": "git", + "url": "https://github.com/dustin10/VichUploaderBundle.git", + "reference": "ac1d4d1d73fa89fe4cb1d00205f01dc7144434ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dustin10/VichUploaderBundle/zipball/ac1d4d1d73fa89fe4cb1d00205f01dc7144434ca", + "reference": "ac1d4d1d73fa89fe4cb1d00205f01dc7144434ca", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "jms/metadata": "^2.4", + "php": "^7.3 || ^8.0", + "symfony/config": "^4.4 || ^5.0", + "symfony/console": "^4.4 || ^5.0", + "symfony/dependency-injection": "^4.4 || ^5.0", + "symfony/event-dispatcher-contracts": "^1.1 || ^2.0", + "symfony/form": "^4.4 || ^5.0", + "symfony/http-foundation": "^4.4 || ^5.0", + "symfony/http-kernel": "^4.4 || ^5.0", + "symfony/mime": "^4.4 || ^5.0", + "symfony/property-access": "^4.4 || ^5.0", + "symfony/string": "^5.0" + }, + "conflict": { + "league/flysystem": "<2.0" + }, + "require-dev": { + "alcaeus/mongo-php-adapter": "^1.2", + "doctrine/doctrine-bundle": "^2.2", + "doctrine/mongodb-odm": "^1.2 || ^2.0", + "doctrine/orm": "^2.7", + "ext-sqlite3": "*", + "knplabs/knp-gaufrette-bundle": "^0.7", + "league/flysystem-bundle": "^2.0", + "league/flysystem-memory": "^2.0", + "matthiasnoback/symfony-dependency-injection-test": "^4.1", + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^9.5", + "symfony/asset": "^4.4 || ^5.0", + "symfony/browser-kit": "^4.4 || ^5.0", + "symfony/css-selector": "^4.4 || ^5.0", + "symfony/doctrine-bridge": "^4.4 || ^5.0", + "symfony/dom-crawler": "^4.4 || ^5.0", + "symfony/framework-bundle": "^4.4 || ^5.0", + "symfony/phpunit-bridge": "^5.2", + "symfony/security-csrf": "^4.4 || ^5.0", + "symfony/translation": "^4.4 || ^5.0", + "symfony/twig-bridge": "^4.4 || ^5.0", + "symfony/twig-bundle": "^4.4 || ^5.0", + "symfony/validator": "^4.4 || ^5.0", + "symfony/var-dumper": "^4.4 || ^5.0", + "symfony/yaml": "^4.4 || ^5.0" + }, + "suggest": { + "doctrine/doctrine-bundle": "For integration with Doctrine", + "doctrine/mongodb-odm-bundle": "For integration with Doctrine ODM", + "doctrine/orm": "For integration with Doctrine ORM", + "doctrine/phpcr-odm": "For integration with Doctrine PHPCR", + "knplabs/knp-gaufrette-bundle": "For integration with Gaufrette", + "league/flysystem-bundle": "For integration with Flysystem", + "liip/imagine-bundle": "To generate image thumbnails", + "ocramius/proxy-manager": "To use lazy services", + "oneup/flysystem-bundle": "For integration with Flysystem", + "symfony/asset": "To generate better links", + "symfony/yaml": "To use YAML mapping" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Vich\\UploaderBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dustin Dobervich", + "email": "ddobervich@gmail.com" + } + ], + "description": "Ease file uploads attached to entities", + "homepage": "https://github.com/dustin10/VichUploaderBundle", + "keywords": [ + "file uploads", + "upload" + ], + "time": "2021-02-26T16:07:21+00:00" + }, { "name": "webmozart/assert", "version": "1.10.0", diff --git a/config/bundles.php b/config/bundles.php index 3e721a1..fa7907d 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -15,4 +15,5 @@ return [ Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], ApiPlatform\Core\Bridge\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], Knp\Bundle\PaginatorBundle\KnpPaginatorBundle::class => ['all' => true], + Vich\UploaderBundle\VichUploaderBundle::class => ['all' => true], ]; diff --git a/config/packages/vich_uploader.yaml b/config/packages/vich_uploader.yaml new file mode 100644 index 0000000..1a39b3b --- /dev/null +++ b/config/packages/vich_uploader.yaml @@ -0,0 +1,12 @@ +vich_uploader: + db_driver: orm + + mappings: + pokemon_image: + uri_prefix: /images/pokemons + upload_destination: '%kernel.project_dir%/public/images/pokemons' + namer: Vich\UploaderBundle\Naming\SmartUniqueNamer + + inject_on_load: false + delete_on_update: true + delete_on_remove: true \ No newline at end of file diff --git a/migrations/Version20210401194203.php b/migrations/Version20210401194203.php new file mode 100644 index 0000000..fa2fc22 --- /dev/null +++ b/migrations/Version20210401194203.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pokemon ADD image_name VARCHAR(255) NOT NULL, ADD image_size INT NOT NULL'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE pokemon DROP image_name, DROP image_size'); + } +} diff --git a/migrations/Version20210401194958.php b/migrations/Version20210401194958.php new file mode 100644 index 0000000..fc45178 --- /dev/null +++ b/migrations/Version20210401194958.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pokemon ADD updated_at DATETIME NOT NULL'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE pokemon DROP updated_at'); + } +} diff --git a/migrations/Version20210401200205.php b/migrations/Version20210401200205.php new file mode 100644 index 0000000..5c0782d --- /dev/null +++ b/migrations/Version20210401200205.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pokemon DROP image_name, DROP image_size, DROP updated_at'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE pokemon ADD image_name VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`, ADD image_size INT NOT NULL, ADD updated_at DATETIME NOT NULL'); + } +} diff --git a/migrations/Version20210401200906.php b/migrations/Version20210401200906.php new file mode 100644 index 0000000..6a4aeee --- /dev/null +++ b/migrations/Version20210401200906.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pokemon ADD image_name VARCHAR(255) NOT NULL, ADD image_size INT NOT NULL, ADD updated_at DATETIME NOT NULL'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE pokemon DROP image_name, DROP image_size, DROP updated_at'); + } +} diff --git a/migrations/Version20210401202700.php b/migrations/Version20210401202700.php new file mode 100644 index 0000000..9dc6474 --- /dev/null +++ b/migrations/Version20210401202700.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pokemon CHANGE image_name image_name VARCHAR(255) DEFAULT NULL'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE pokemon CHANGE image_name image_name VARCHAR(255) CHARACTER SET utf8mb4 NOT NULL COLLATE `utf8mb4_unicode_ci`'); + } +} diff --git a/migrations/Version20210401202758.php b/migrations/Version20210401202758.php new file mode 100644 index 0000000..735bd02 --- /dev/null +++ b/migrations/Version20210401202758.php @@ -0,0 +1,31 @@ +addSql('ALTER TABLE pokemon CHANGE image_size image_size INT DEFAULT NULL'); + } + + public function down(Schema $schema) : void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('ALTER TABLE pokemon CHANGE image_size image_size INT NOT NULL'); + } +} diff --git a/src/Controller/PokemonController.php b/src/Controller/PokemonController.php index 26e25d0..733ece6 100644 --- a/src/Controller/PokemonController.php +++ b/src/Controller/PokemonController.php @@ -10,12 +10,24 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; use Knp\Component\Pager\PaginatorInterface; +use Vich\UploaderBundle\Templating\Helper\UploaderHelper; /** * @Route("/pokemon") */ class PokemonController extends AbstractController { + + /** + * @var UploaderHelper + */ + protected $uploaderHelper; + + public function __construct(UploaderHelper $uploaderHelper) + { + $this->uploaderHelper = $uploaderHelper; + } + /** * @Route("/", name="pokemon_index", methods={"GET"}) */ @@ -70,8 +82,11 @@ class PokemonController extends AbstractController */ public function show(Pokemon $pokemon): Response { + $imagePath = $this->uploaderHelper->asset($pokemon, 'imageFile'); + return $this->render('pokemon/show.html.twig', [ 'pokemon' => $pokemon, + 'imagePath' => $imagePath, ]); } diff --git a/src/Entity/Pokemon.php b/src/Entity/Pokemon.php index 41a8985..6ef97da 100644 --- a/src/Entity/Pokemon.php +++ b/src/Entity/Pokemon.php @@ -8,9 +8,12 @@ use ApiPlatform\Core\Annotation\ApiResource; use Symfony\Component\Serializer\Annotation\Groups; use ApiPlatform\Core\Annotation\ApiFilter; use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter; +use Symfony\Component\HttpFoundation\File\File; +use Vich\UploaderBundle\Mapping\Annotation as Vich; /** * @ORM\Entity(repositoryClass=PokemonRepository::class) + * @Vich\Uploadable * @ApiResource( * normalizationContext={ * "groups"={"pokemon_read"} @@ -82,6 +85,36 @@ class Pokemon */ private $numero; + /** + * NOTE: This is not a mapped field of entity metadata, just a simple property. + * + * @Vich\UploadableField(mapping="pokemon_image", fileNameProperty="imageName", size="imageSize") + * + * @var File|null + */ + private $imageFile; + + /** + * @ORM\Column(type="string",nullable=true) + * + * @var string|null + */ + private $imageName; + + /** + * @ORM\Column(type="integer",nullable=true) + * + * @var int|null + */ + private $imageSize; + + /** + * @ORM\Column(type="datetime") + * + * @var \DateTimeInterface|null + */ + private $updatedAt; + public function getId(): ?int { return $this->id; @@ -194,4 +227,49 @@ class Pokemon return $this; } + + /** + * If manually uploading a file (i.e. not using Symfony Form) ensure an instance + * of 'UploadedFile' is injected into this setter to trigger the update. If this + * bundle's configuration parameter 'inject_on_load' is set to 'true' this setter + * must be able to accept an instance of 'File' as the bundle will inject one here + * during Doctrine hydration. + * + * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile|null $imageFile + */ + public function setImageFile(?File $imageFile = null): void + { + $this->imageFile = $imageFile; + + if (null !== $imageFile) { + // It is required that at least one field changes if you are using doctrine + // otherwise the event listeners won't be called and the file is lost + $this->updatedAt = new \DateTimeImmutable(); + } + } + + public function getImageFile(): ?File + { + return $this->imageFile; + } + + public function setImageName(?string $imageName): void + { + $this->imageName = $imageName; + } + + public function getImageName(): ?string + { + return $this->imageName; + } + + public function setImageSize(?int $imageSize): void + { + $this->imageSize = $imageSize; + } + + public function getImageSize(): ?int + { + return $this->imageSize; + } } diff --git a/src/Form/PokemonType.php b/src/Form/PokemonType.php index 6bf1b12..d5b0360 100644 --- a/src/Form/PokemonType.php +++ b/src/Form/PokemonType.php @@ -10,6 +10,7 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +use Vich\UploaderBundle\Form\Type\VichFileType; class PokemonType extends AbstractType { @@ -39,6 +40,14 @@ class PokemonType extends AbstractType 'choice_label' => 'name', 'label' => 'Generation' )) + ->add('imageFile', VichFileType::class, [ + 'required' => false, + 'allow_delete' => true, + 'delete_label' => 'Remove file', + 'download_uri' => true, + 'download_label' => true, + 'asset_helper' => true, + ]) ; } diff --git a/symfony.lock b/symfony.lock index 5e21b47..c744a98 100644 --- a/symfony.lock +++ b/symfony.lock @@ -105,6 +105,9 @@ "friendsofphp/proxy-manager-lts": { "version": "v1.0.3" }, + "jms/metadata": { + "version": "2.5.0" + }, "knplabs/knp-components": { "version": "v3.0.2" }, @@ -553,6 +556,18 @@ "twig/twig": { "version": "v3.3.0" }, + "vich/uploader-bundle": { + "version": "1.5", + "recipe": { + "repo": "github.com/symfony/recipes-contrib", + "branch": "master", + "version": "1.5", + "ref": "c4f5755b37fb65b9c6a3cbdae91205c15a137ed4" + }, + "files": [ + "config/packages/vich_uploader.yaml" + ] + }, "webmozart/assert": { "version": "1.10.0" }, diff --git a/templates/pokemon/show.html.twig b/templates/pokemon/show.html.twig index af99f61..769c7a2 100644 --- a/templates/pokemon/show.html.twig +++ b/templates/pokemon/show.html.twig @@ -5,6 +5,10 @@ {% block body %}

Pokemon

+ {% if imagePath %} + pokemon-img + {% endif %} +