Symfony VichUploaderBundle: File name could not be generated - php

I am using VichUploader to upload files within a symfony project. In configuration i use (copied from documentation):
service: vich_uploader.namer_property
options: { property: 'slug'}
In my entity i generate the slugs automatically with Gedmo/Sluggable:
/**
* #Gedmo\Slug(fields={"title"}, updatable=false)
* #ORM\Column(type="string", length=100, nullable=false)
*/
protected $slug;
But when trying to save the entity i get the following error 500:
File name could not be generated: property slug is empty.
If i set the property to 'title' it works. Did i forget a configuration parameter or something else to get it working with the Gedmo slug?

I'm having the same issue at the moment, as a workaround, I've slightly changed the slug getter in the entity class:
use Gedmo\Sluggable\Util\Urlizer;
class Event
{
// ...
/**
* #var string
*
* #Gedmo\Slug(fields={"name"})
* #ORM\Column(name="slug", type="string", length=128, unique=true)
*/
private $slug;
// ...
public function getSlug()
{
if (!$this->slug) {
return Urlizer::urlize($this->getName());
}
return $this->slug;
}
}
That did the trick.
Unfortunately, there're a couple of drawbacks:
If you ever want to update sluggable behaviour in the annotation to include additional properties, you'll have to update the getter as well.
This method lacks a check against the database: if there's already a record in the database with the same name urlizer in the getter won't be able to add an increment to the file name, previously saved file may be overwritten! As a workaround, you can add unique=true to sluggable properties.

VichUploader listens to the prePersist and preUpdate events, whereas Sluggable listens to the onFlush event. Because prePersist and preUpdate are called before onFlush, it isn't possible to do this purely using configuration.
However, if your file field is nullable, you can work around it by changing your controller code. When you receive the submitted form with the file, remove the file, save the entity, then re-add the file and save the entity again. On the second save, the slug will already be set, so VichUploader will be able to save the file fine.
if ($form->isSubmitted() && $form->isValid()) {
if ($file = $entity->getFile()) {
$entity->setFile(null);
}
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
if ($file) {
$entity->setFile($file);
$em->persist($entity);
$em->flush();
}
// ...
}
This only works when adding a new file. If you subsequently change the slug and resave the entity without uploading a new file, the filename is not updated.

I was having the same problem uploading a document for which I needed the fileName to be the slug.
I was using Gedmo annotations to generate the slug, however this only triggers on flush and the vichUploader namer is triggered upon persist.
The easiest way for me to get this working was to not use the Gedmo\Sluggable annotation but rather create a prePersist listener on my Document object and use the Cocur\Slugify library.
So here is the code.
My Document Entity:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Symfony\Component\HttpFoundation\File\File;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity(repositoryClass="App\Repository\DocumentRepository")
* #Vich\Uploadable
* #ORM\EntityListeners({"App\Listeners\DocumentListener"})
*/
class Document
{
use TimestampableEntity;
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=100, nullable=false)
*/
private $fileName;
/**
* #Vich\UploadableField(mapping="document", fileNameProperty="fileName")
* #var File
*/
private $documentFile;
/**
* #ORM\Column(type="string", length=100, unique=true)
*/
private $slug;
/**
*/
public function getDocumentFile(): ?File
{
return $this->documentFile;
}
/**
* #param File $documentFile
* #return Document
* #throws \Exception
*/
public function setDocumentFile(File $documentFile = null): Document
{
$this->documentFile = $documentFile;
if($documentFile){
$this->updatedAt = new \DateTimeImmutable();
}
return $this;
}
public function getId(): ?int
{
return $this->id;
}
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): self
{
$this->slug = $slug;
return $this;
}
/**
* #return mixed
*/
public function getFileName()
{
return $this->fileName;
}
/**
* #param mixed $fileName
*/
public function setFileName($fileName): void
{
$this->fileName = $fileName;
}
}
And the listener :
namespace App\Listeners;
use App\Entity\Document;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Cocur\Slugify\Slugify;
class DocumentListener
{
public function prePersist(Document $document, LifecycleEventArgs $args)
{
$slugify = new Slugify();
if(!empty($document->getDocumentFile())){
$originalName = pathinfo($document->getDocumentFile()->getClientOriginalName(), PATHINFO_FILENAME);
$slug = $slugify->slugify($originalName);
$document->setSlug($slug);
}
}
}
So far I have not had any problems.
Let me know if this works for you

By default the doctrine extensions bundle does not attach any listener:
http://symfony.com/doc/current/bundles/StofDoctrineExtensionsBundle/index.html#activate-the-extensions-you-want
You should configure it to get sluggable working:
stof_doctrine_extensions:
orm:
default:
sluggable: true

Related

Doctrine lifecycle callbacks not working with EasyAdminBundle

I am using Symfony 4 with EasyAdminBundle to create a simple administrative interface. I'm having a few problems trying to automatically set the value for createdAt and updatedAt columns. When creating/updating an entity within EasyAdmin, the configured Doctrine lifecycle callbacks are not called. For example, here's a simple entity that is managed with EasyAdmin, note the lifecycle callback hooks:
<?php
namespace App\Entity;
/**
* #ORM\Entity(repositoryClass="App\Repository\ProductRepository")
* #ORM\HasLifecycleCallbacks
*/
class Product
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
// Additional column configuration removed for brevity
/**
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #ORM\Column(type="datetime", nullable=true)
*/
private $updatedAt;
// Additional getters/setters removed for brevity
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->createdAt;
}
public function setCreatedAt(?\DateTimeInterface $createdAt): self
{
$this->createdAt = $createdAt;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(?\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* #ORM\PrePersist
*/
public function onPrePersist(): void
{
$this->createdAt = new \DateTime();
}
/**
* #ORM\PreUpdate
*/
public function onPreUpdate(): void
{
$this->updatedAt = new \DateTime();
}
}
When I create a new product within EasyAdmin, onPrePersist() is not called, and when I edit an existing product with EasyAdmin, onPreUpdate() is not called.
If I create a new product the "traditional" way, the lifecycle callbacks work exactly as expected. For example:
<?php
$product = new Product();
$product->setTitle('Test Product');
$product->setDescription('Test description');
// Doctrine lifecycle callbacks work as expected
$this->getDoctrine()->getManager()->persist($product);
Is EasyAdminBundle bypassing Doctrine lifecycle callbacks? If so, why? How do I utilize Doctrine lifecycle callbacks within Doctrine entities managed by EasyAdminBundle?
I understand there is documentation to do something similar with an AdminController:
https://symfony.com/doc/master/bundles/EasyAdminBundle/book/complex-dynamic-backends.html#update-some-properties-for-all-entities
But again, why do I need to do this when we already have Doctrine lifecycle callbacks. My other problem with using an AdminController and extending various methods, persistEntity() within AdminController is never called either.
What am I missing?
Any help would be greatly appreciated! Cheers!

EasyAdminBundle and VichUploaderBundle - Error: The expected argument of type"", "Symfony\Component\HttpFoundation\File\UploadedFile" given

I'm using EasyAdminBundle for entity management and to upload images I want to useVichUploaderBundle.
Following the documentation configure the Bundle:
https://github.com/javiereguiluz/EasyAdminBundle/blob/master/Resources/doc/integration/vichuploaderbundle.rst
I do not use annotations but yml as described in the documentation:
https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/mapping/yaml.md
My code looks like this:
//app/config/config.yml
vich_uploader:
db_driver: orm
mappings:
torneo_images:
uri_prefix: '%app.path.torneo_images%'
upload_destination: '%kernel.root_dir%/../web/uploads/images/torneos'
..........
easy_admin:
form:
fields:
- logo
- { property: 'imageFile', type: 'file' }
The yml configuration file:
//BackendBundle/Resources/config/doctrine/Torneos.orm.yml
......
logo:
type: string
nullable: true
length: 255
options:
fixed: false
imageFile:
mapping: torneo_images
filename_property: logo
Add to Entity
//BackendBundle/Entity/Torneos.orm.yml
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\PropertyMapping as Vich;
namespace BackendBundle\Entity;
.......
/**
* #var string
*/
private $logo;
/**
* #var File
*/
private $imageFile;
.......
/**
* Set logo
*
* #param string $logo
*
* #return Torneos
*/
public function setLogo($logo)
{
$this->logo = $logo;
return $this;
}
/**
* Get logo
*
* #return string
*/
public function getLogo()
{
return $this->logo;
}
/**
* 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 $image
*
* #return Torneos
*/
public function setImageFile(File $logo = null)
{
$this->imageFile = $logo;
// VERY IMPORTANT:
// 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
//if ($image) {
// if 'updatedAt' is not defined in your entity, use another property
// $this->updatedAt = new \DateTime('now');
//}
return $this;
}
/**
* #return File|null
*/
public function getImageFile()
{
return $this->imageFile;
}
Also add this code (I'm not sure if it's correct)
//BackendBundle/Resources/config/vich_uploader/Torneos.orm.yml
BackendBundle\Entity\Torneos:
imageFile:
mapping: torneo_images
filename_property: logo
Can anyone give me some idea to fix it?
The solution was quite simple.
The error occurs because the use are placed before thenamespace in the controller.
namespace BackendBundle\Entity;
Regards

Symfony Doctrine2 manyToMany relationship not removed - Specific to SQLite

I have several classes using a Taggable trait to set up a tag system common to several doctrine entities (Project, Note, ...).
The relationship between these entities and these tags is a ManyToMany relationship that I can not make multi-directional.
My problem: When I delete a Project entity, it is removed from the project table, but the relationships in the project_tag table between this project and the tags are not deleted. Then, if I create a new Project entity, an exception is thrown.
An exception exists while executing 'INSERT INTO project_tag (project_id, tag_id) VALUES (?,?)' With params [2, 4]:
SQLSTATE [23000]: Integrity constraint violation: 19 UNIQUE constraint failed: project_tag.project_id, project_tag.tag_id
Entities :
Tag
/**
* Tag
*
* #ORM\Table(name="tag")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TagRepository")
*/
class Tag
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* #ORM\Column(name="last_use_at", type="datetime", nullable=false)
* #var \DateTime
*/
private $lastUseAt;
public function __construct()
{
$this->lastUseAt = new \DateTime();
}
public function __toString()
{
return $this->name;
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Tag
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName(): string
{
return $this->name;
}
/**
* #return \DateTime
*/
public function getLastUseAt(): \DateTime
{
return $this->lastUseAt;
}
/**
* #param \DateTime $lastUseAt
*/
public function setLastUseAt(\DateTime $lastUseAt)
{
$this->lastUseAt = $lastUseAt;
}
}
Taggable
trait Taggable
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Tag", cascade={"persist"})
*/
protected $tags;
/**
* Add tag
*
* #param Tag $tag
*
* #return $this
*/
public function addTag(Tag $tag)
{
$tag->setLastUseAt(new \DateTime());
$this->tags[] = $tag;
return $this;
}
/**
* Remove tag
*
* #param Tag $tag
*/
public function removeTag(Tag $tag)
{
$this->tags->removeElement($tag);
}
/**
* Get tags
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
}
Project
/**
* Project
*
* #ORM\Table(name="project")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ProjectRepository")
*/
class Project
{
use Taggable;
}
Note
class Note
{
use Taggable;
}
Is this the only solution or is my annotation incomplete / incorrect?
I tried with JoinColumns, JoinTable and onDelete = "cascade" but nothing works.
In the meantime, I dodged the problem with this instruction placed before the suppresion.
$project->getTags()->clear();
Full code of the action in the controller :
/**
* #Route("/project/{id}/delete", name="project_delete")
*/
public function deleteAction($id) {
$em = $this->getDoctrine()->getManager();
$project = $em->getRepository('AppBundle:Project')->find($id);
if(!$project) {
return $this->redirectToRoute('index');
}
$project->getTags()->clear();
$em->remove($project);
$em->flush();
return $this->redirectToRoute('index');
}
I think I found a better solution: you can set the PRAGMA within Doctrine configuration. Like:
doctrine:
dbal:
# configure these for your database server
driver: 'pdo_sqlite'
#server_version: '5.7'
#charset: utf8mb4
#default_table_options:
#charset: utf8mb4
#collate: utf8mb4_unicode_ci
url: '%env(resolve:DATABASE_URL)%'
options:
'PRAGMA foreign_keys': 'ON'
I just tried it on my Symfony 4 application, re-created the database and tested using DB Browser for SQLite and it works as I expected.
Hope this helps
I managed to fix the problem. Here's my solution working for SQLite conections.
Create an eventListener listening on the kernel.request event :
namespace AppBundle\EventListener;
use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class RequestListener
{
/**
* #var Registry
*/
private $doctrine;
public function __construct(Registry $doctrine)
{
$this->doctrine = $doctrine;
}
public function onKernelRequest(GetResponseEvent $event)
{
$this->doctrine->getConnection()->exec('PRAGMA foreign_keys = ON');
}
}
Service declaration
app.event_listener.request_listener:
class: AppBundle\EventListener\RequestListener
arguments:
- '#doctrine'
tags:
- { name: kernel.event_listener, event: kernel.request }
I think the problem is that you have your trait Taggable set as the owning side of the ManyToMany relationship but your are deleting the inverse side and expecting something to happen as a result. Doctrine will only check the owning side of the relationship in order to persist any changes. See here for docs on this.
You can solve by making the Taggable the inverse side of each of your relationships, or by manually telling doctrine to delete the owning side.
The first solution will probably not work for you since you won't (easily) specify multiple inverse sides. (Are you sure a trait is the right way to go for this??)
The second solution is easy. In your entities like Project for your deleteTag($tag) function, call a delete function on the owning side (e.g., deleteProject($project). You will have to create if one does not exist.
class Project
{
use Taggable;
public function deleteTag($tag)
{
$this->tags->removeElement($tag);
// persist on the owning side
$tag->deleteProject($this);
}
}
EDIT:
After seeing full code, it looks like you are deleting correctly. Now you need to tell doctrine to carry that through. See this post for full details, but basically you can change your trait to this:
trait Taggable
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(
* targetEntity="AppBundle\Entity\Tag",
* cascade={"persist"},
* onDelete="CASCADE"
* )
*/
protected $tags;
// ...
}

Symfony 3 FOS Rest + JMS Serializer groups

My composer.json (part of it):
{
"require": {
"symfony/symfony": "3.1.*",
"jms/serializer-bundle": "^1.1",
"friendsofsymfony/rest-bundle": "^2.1"
}
}
I have some entities which I'd like to return partial data for the list action and complete for the find action. For so, I have these files:
Product.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
/**
* #ORM\Entity
* #ORM\Table(name="represented")
* #JMS\ExclusionPolicy("ALL")
*/
class Product
{
/**
* #var integer
* #ORM\Column(type="integer", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(type="string", nullable=false, length=48)
*/
protected $name;
/**
* #var Group
* #ORM\ManyToOne(targetEntity="Group", inversedBy="products")
* #ORM\JoinColumn(name="group_id", referencedColumnName="id")
*/
protected $group;
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setGroup(Group $group)
{
$this->group = $group;
}
public function getGroup()
{
return $this->group;
}
}
ProductController.php
<?php
namespace AppBundle\Controller;
use FOS\RestBundle\Controller\Annotations\Get;
use FOS\RestBundle\Controller\FOSRestController;
use AppBundle\Entity\Product;
class ProductController extends FOSRestController
{
/**
* #Get("/product", name="list_products")
*/
public function listAction()
{
$products = $this->getDoctrine()
->getRepository('AppBundle:Product')
->findBy([], [ 'name' => 'ASC' ]);
$view = $this->view($products);
return $this->handleView($view);
}
/**
* #Get("/product/{id}", requirements={"id" = "\d+"}, name="get_product")
*/
public function getAction($id)
{
$em = $this->getDoctrine()->getManager();
$product = $em->getRepository('AppBundle:Product')
->find($id);
if ( ! $product) {
$error = [
'error' => 'Product not found'
];
$view = $this->view($error, 404);
} else {
$view = $this->view($product);
}
return $this->handleView($view);
}
}
I would like to be able to not show the group property on the list result. For this I have tried a few of approaches, mainly with groups.
Just use configure the group name for the properties I want to show
on my list with Groups({"List"}) and refer this group on the
controller with #View(serializerGroups={"List"}). But this had no
affect as all properties are visible.
Configure #ExclusionPolicy("all") for the entire entity didn't
work as well.
In addition to ExclusionPolicy, #Expose to all properties I wanted
to show in some or all groups, but that made all properties marked
to be shown.
I also tried some more variants of these, but nothing that change the results.
End of your controller action should not be only:
return $this->handleView($view);
But for getting a group or groups working you need to activate them. On top of the class you need to add FOSRest Context, not JMS context:
use FOS\RestBundle\Context\Context;
and in the controller actions before returning view:
$view = $this->view($products, 200);
$context = new Context();
$context->setGroups(['list']);
$view->setContext($context);
return $this->handleView($view);
This will work for Symfony 3.2 and FOSRest 2.1 and 2.0.
Link for upgrading documentation for FOSRest:
https://github.com/FriendsOfSymfony/FOSRestBundle/blob/master/UPGRADING-2.0.md
If you use #Exclude then this should work. Another option I have used is to make a small addition to config.yml:
fos_rest:
serializer:
groups: ['Default']
This requires that the entity properties be in the group Default in order to be serialized. If you have no #Groups annotation on a property then it will be in Default, but as soon as you add the #Groups annotation then it will no longer be in the Default group (unless you specifically add it.)
This allowed the default serialization process to include all the entity fields except for those with a #Groups annotation, and then elsewhere I could serialize with both Default and my other groups selected if I wanted everything to be included.
// Function in the Controller class
public function getAction(MyEntity $me) {
// Use automatic serializer, only things in 'Default' group are included
return $me;
}

How to Use Gaufrette and Symfony 3.0

I have an issue from figuring out how to use the Symfony 3.0 with Gaufete in order to Upload into an s3 bucket.
According to the documentation: https://github.com/KnpLabs/KnpGaufretteBundle
I have set the config.yml:
knp_gaufrette:
adapters:
photostorage:
amazon_s3:
amazon_s3_id: amazonS3
bucket_name: '%save_location%'
options:
directory: 'symphotest'
And the services.yml:
services:
amazonS3:
class: Aws\S3\S3Client
factory_class: Aws\S3\S3Client
factory_method: 'factory'
arguments:
key: %amazon_s3.key%
secret: %amazon_s3.secret%
region: %amazon_s3.region%
And because I want to use custom enviromental variables any sort of configuration I pass it a file params.php:
$container->setParameter('save_type','s3');
$container->setParameter('save_location',getenv('BUCKET'));
$container->setParameter('aws_key',getenv('S3_ACCESS'));
$container->setParameter('aws_secret_key',getenv('S3_SECRET'));
Where I Include it on the top of the config.yml:
imports:
- { resource: params.php }
- { resource: security.yml }
- { resource: services.yml }
I have made an Entity names Images.php:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
use Gaufrette\Adapter\AwsS3 as AwsS3Adapter;
use Gaufrette\Filesystem;
/**
* #ORM\Entity
* #ORM\Table(name="images")
* #ORM\HasLifecycleCallbacks
*/
class Images
{
/**
* #ORM\Column(type="string", length=60)
* #ORM\Id
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="AppBundle\Doctrine\AutoIdGenerate")
*/
private $id;
/**
* #ORM\Column(type="string", length=100)
*/
private $name;
/**
* #ORM\Column(type="string", length=100)
*/
private $name_small;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\ImageGroups", inversedBy="images")
*/
private $users;
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
private $tmp;
private $path;
public function getFile()
{
return $file;
}
public function setFile(UploadedFile $file = null)
{
$this->file=$file;
};
public function __construct()
{
//IDK what to do here
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->getFile())
{
$filename = sha1(uniqid(gethostname(), true));
$this->name = $filename.'.'.$this->getFile()->guessExtension();
$this->$name_small='small'.$filename.'.'.$this->getFile()->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->getFile())
{
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->getFile()->move($this->getUploadRootDir(), $this->path);
// check if we have an old image
if (isset($this->temp))
{
// delete the old image
unlink($this->getUploadRootDir().'/'.$this->temp);
// clear the temp image path
$this->temp = null;
}
$this->file = null;
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
$file = $this->getAbsolutePath();
if ($file)
{
//Do stuff for Deleting
}
}
/**
* Get id
*
* #return string
*/
public function getId()
{
return $this->id;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Get nameSmall
*
* #return string
*/
public function getNameSmall()
{
return $this->name_small;
}
}
But I do not know how can I get the S3 Adapter and client because in this example:https://github.com/KnpLabs/Gaufrette/blob/master/doc/adapters/awsS3.md
In initiates the s3 client and filesystem. But on Symfony 3.0 I've already configured them on config.yml. Therefore there must be another way to get them.
All I want is an example of usage.
I recommend you read this article: https://blog.fortrabbit.com/new-app-cloud-storage-s3
Is is a quick start guide that talks about why you could use decentralized storage and covers the following topics:
How to sign up for an AWS account
How to create you first S3 bucket storage container — name space for your files
How to set up proper permissions — safe access with additional credentials
Alternatively, I have had good experiences with the LeaguePHP Adapter:
League\Flysystem\AwsS3v3
It provides a simple api for using the amazon web services for files and stuff! It's compatible standalone or using Symfony or laravel. Check out the documentation. You can see the methods from the source folder.
Don't inject a service into an entity: it is bad practice.
Use Doctrine event subscribers instead: as described on Symfony docs
# app/config/services.yml
services:
# ...
AppBundle\EventListener\SearchIndexerSubscriber:
tags:
- { name: doctrine.event_subscriber }
Event subscriber:
// src/AppBundle/EventListener/SearchIndexerSubscriber.php
namespace AppBundle\EventListener;
use AppBundle\Entity\Product;
use Doctrine\Common\EventSubscriber;
// for Doctrine < 2.4: use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
class SearchIndexerSubscriber implements EventSubscriber
{
public function getSubscribedEvents()
{
return [
Events::postPersist,
Events::postUpdate,
];
}
public function postUpdate(LifecycleEventArgs $args)
{
$this->index($args);
}
public function postPersist(LifecycleEventArgs $args)
{
$this->index($args);
}
public function index(LifecycleEventArgs $args)
{
$entity = $args->getObject();
// perhaps you only want to act on some "Product" entity
if ($entity instanceof Product) {
$entityManager = $args->getObjectManager();
// ... do something with the Product
}
}
}

Categories