Simple example of the entity file to be uploaded with OneupUploaderBundle - symfony

I am try to use OneupUploaderBundle for uploading files. I read the documentation of this bundle many times but I did not manage to find any simple example of an entity for the file to be uploaded. My expectation is a class definition similar to the VichUploaderBundle:
<?php
namespace Minn\AdsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity
* #Vich\Uploadable
*/
class MotorsAdsFile {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #Assert\File(
* maxSize="5M",
* mimeTypes={"image/png", "image/jpeg"}
* )
* #Vich\UploadableField(mapping="motors_files", fileNameProperty="filename")
* note: This is not a mapped field of entity metadata, just a simple property.
* #var File $file
*/
protected $file;
/**
* #ORM\Column(type="string", length=255, name="filename")
* #var string $filename
*/
protected $filename;
/**
* #ORM\Column(type="string", length=255, name="name")
* #var string $name
*/
protected $name;
// ...
}
Hope my question is clear...
I am really interested by this bundle since it supports jQuery.
Thanks,

There is no such thing as a predefined entity or ORM-handling. This has several reasons. This bundle can not foresee the projects need in terms of upload logic. If someone wants to store files to the database or not is entirely their own choice and should not be forced by a third party bundle. The thing the OneupUploaderBundle provides is a backend for the most common frontend uploaders there are.
Disclaimer: I've shorty copied and extended an answer that was already present in the GitHub issue tracker of this bundle. There you'll find quite a lot of insights on how and why this bundle is what it is now.
Given that you have already installed a working Symfony2 project, follow the installation instructions of the this bundle. I think step 1 and 2 should not be a problem, so lets hook directly to step 3, the configuration.
You said, you tried to integrate the jQuery File Uploader, so lets create a mapping for it.
Open the file app/config/config.yml and add the following lines to the end of it.
oneup_uploader:
mappings:
gallery:
frontend: blueimp
And of course, don't forget to add the following lines to app/config/routing.yml.
oneup_uploader:
resource: .
type: uploader
So much for the configuration. For the sake of simplicity we will alter the AcmeDemoBundle.
Open the file src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig and delete everything between {% block content %} and {% endblock %}. We don't need this anymore.
Now insert the following:
<script type="text/javascript" src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="https://rawgithub.com/blueimp/jQuery-File-Upload/master/js/vendor/jquery.ui.widget.js"></script>
<script type="text/javascript" src="https://rawgithub.com/blueimp/jQuery-File-Upload/master/js/jquery.iframe-transport.js"></script>
<script type="text/javascript" src="https://rawgithub.com/blueimp/jQuery-File-Upload/master/js/jquery.fileupload.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#fileupload').fileupload({});
});
</script>
<input id="fileupload" type="file" name="files[]" data-url="{{ oneup_uploader_endpoint('gallery') }}" multiple />
Point your browser to the root directory of this application (app_dev.php). You'll see an input field just like expected and you can now upload some images (for example). The files will be stored in web/uploads/gallery every single one with a unique filename. Note that we used some CDNs to serve the JavaScript files needed for this.
At this point, you already have a working upload form. But besides uploading files to the uploads directory, it does nothing.
This is where the Next step section in the documentation comes in handy.
As I understand your question, you want to create an entity which stores a file path of a related file in it.
To do so, create your Entity class including all required fields.
<?php
namespace Minn\AdsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class MotorsAdsFile {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="string", length=255, name="filename")
* #var string $filename
*/
protected $filename;
// ...
}
Then create an EventListener as described in the documentation of this bundle.
<?php
namespace Acme\HelloBundle\EventListener;
use Oneup\UploaderBundle\Event\PostPersistEvent;
use Minn\AdsBundle\Entity\MotorsAdsFile;
class UploadListener
{
protected $manager;
public function __construct(EntityManager $manager)
{
$this->manager = $manager;
}
public function onUpload(PostPersistEvent $event)
{
$file = $event->getFile();
$object = new MotorsAdsFile();
$object->setFilename($file->getPathName());
$this->manager->persist($object);
$this->manager->flush();
}
}
And of course register your event listener.
<services>
<service id="acme_hello.upload_listener" class="Acme\HelloBundle\EventListener\UploadListener">
<argument type="service" id="doctrine.orm.entity_manager" />
<tag name="kernel.event_listener" event="oneup_uploader.post_persist" method="onUpload" />
</service>
</services>
At this point the EventListener will be called as soon as a new file was uploaded through the configured mapping.
It takes this file, creates a new object of MotorsAdsFile and stores the file path to the filename property, persists and flushes it to the underlying database.
As I can't predict your actual logic, this is the most basic example I can think of. You're of course free to do whatever is needed in the event listener. (In case you need to store this object to another entity or the like.)
You'll find a bunch of other topics in the Next steps section of the documentation. For example how you'd change the naming strategy of the uploaded files or how to enable chunked uploads in case you need to upload big files.

Related

symfony display image thumbnail for file in private directory

I have Symfony 6.2 application in which logged user may upload images to server. Because images are non public, I upload this images to /var/images directory.
In config/services.yaml I have
parameters:
card_directory: '%kernel.project_dir%/var/images'
and for upload service I use
App\Service\ImageUploader:
arguments:
$targetDirectory: '%card_directory%'
upload images is function.
But now I need serve this images to twig template for generating 4 column thumnail gallery. How I should generate path to this images in twig templates?
Finally I use this code for server private images with Controller for Card entity, which has image
<?php
namespace App\Controller;
use App\Entity\Card;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\Routing\Annotation\Route;
class ServePrivateImageController extends AbstractController
{
/**
* Returns a private image for card file for display.
*
* #Route("/serve-private-image/{id}", name="serve_private_image", methods="GET")
* #param Card $card
* #return BinaryFileResponse
*/
public function privateImageServe(Card $card): BinaryFileResponse
{
return $this->fileServe($card->getImage());
}
/**
* Returns a private file for display.
*
* #param string $path
* #return BinaryFileResponse
*/
private function fileServe(string $path): BinaryFileResponse
{
$absolutePath = $this->getParameter('card_directory') . '/' . $path;
return new BinaryFileResponse($absolutePath);
}
}
To answer your question, you would simply refer to your route within twig.
<div class="row">
{% for file in files %}
<div class="col-md-3">
<img src="{{ path('serve_private_image', {id: file.id}) }}" />
</div>
{% endfor %}
But as far as serving the resized images, LiipImageBundle does what you're looking for, and much more:
https://github.com/liip/LiipImagineBundle
Although it takes a few minutes to set up (because of the integration with flysystem), once it's working you can even keep your (secure) images on s3 and it handles all the downloading and local caching, rotating the resized issues, etc.

exception id not found, dump show entity with isInitialized false and all attributes null

I got a Cours (it's french for lesson) entity, and this entity have a "prof" attribute that is a manytoone relation with a Prof (teacher in french) entity. This Prof entity in itself extend an Eleve (student) entity (a prof is an eleve with more attributes and methods).
I created a prof and a cours with fixtures, just to test my app. Then, in a CourseController, I tried to send my cours and prof informations to a view. To do so, I load my cours, then I try to access my prof through my cours.
When I try to show the cours informations, everything is fine. But when I try to access the prof, nothing works anymore. I got an error that tells me that id column doesn't exists, and when I try to dump the prof, it tells me isInitialized is false, and every attributes is null.
I found other stackoverflow (or other forums) topics with similar problems where people explain that symfony do lazy loading, and doesn't load the related entity this way it gets faster. But all solutions proposed doesn't work or aren't good choices in my case.
One of the solution seems to be to set fetching to eager in my manytoone relation. But the problem is that prof itself has many manytoone or manytomany or other relations, and each of this entities has to allow access to entities related (so fetching eager too), and so, each time I'll want to get an entity, I'll load the entire database. And I just want to load what I need, which will change in each controller and each methods.
Another solution proposed is to call some methods of my related object (prof in this situation), to tell doctrine we need to access it, this way doctrine load it. But this result with the exception again (telling id column is not found).
in CoursController :
/**
* #Route("/cours/{slug}", name="presentationCours")
*/
public function presentationCours(Cours $cours)
{
$prof = $cours->getProf();
// $prof->getLogin(); THIS result with exception
dump($prof); // THIS shows all null values
die();
return $this->render('cours/presentation.html.twig', [
"cours" => $cours,
"prof" => $prof
]);
}
note : if you wonder how I get the $cours var without telling symfony where to find it, it can find it just with the slug in the route. It's new in symfony 4 (not sure it's new but I think). I'm sure it works, I did it many times and a dump($cours) perfectly works.
the twig file :
{% extends 'base.html.twig' %}
{% block title %}{{ cours.nom }}{% endblock %}
{% block body %}
<p>
{{ cours.nom }}
</p>
<p>
{{ cours.description }}
</p>
<p>
{{ prof.login }} {# THIS result with exception #}
</p>
{% endblock %}
part of the Eleve entity (the entity which is parent of prof : prof extends eleve)
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\EleveRepository")
*/
class Eleve
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
*/
private $login;
...
}
part of the prof entity :
<?php
namespace App\Entity;
use App\Entity\Eleve;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="App\Repository\ProfRepository")
*/
class Prof extends Eleve
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $introduction;
/**
* #ORM\Column(type="text")
*/
private $description;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Cours", mappedBy="prof")
*/
private $cours;
part of the cours entity :
<?php
namespace App\Entity;
use Cocur\Slugify\Slugify;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="App\Repository\CoursRepository")
* #ORM\HasLifecycleCallbacks
*/
class Cours
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Prof", inversedBy="cours")
* #ORM\JoinColumn(nullable=false)
*/
private $prof;
in the database with phpmyadmin :
part of the cours table :
part of the prof table :
the exception (without the dump) :
the dump(prof) result :
Thank you for your time.
EDIT
Problem solved ! As jeroen told, the problem was about inheritance.
Here is what I did to manage to solve it :
<?php
namespace App\Entity;
use App\Entity\Prof;
use App\Entity\Eleve;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\InheritanceType;
use Doctrine\ORM\Mapping\DiscriminatorMap;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping\DiscriminatorColumn;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="App\Repository\EleveRepository")
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"eleve" = "Eleve", "prof" = "Prof"})
*/
class Eleve
{
I added the lines inheritancetype, discriminatorcolumn, and discriminatormap. This, because I chosed the class table inheritance, but you could chose the mapped superclass solution, or the single table inheritance solution.
For those who, like me, would have made a migration before changing the classes, I had a problem when I added the class table inheritance. Doctrine refused to migrate after that, an exception told me there was a foreign key problem. I'm not sure this is the good way, but I deleted my migration files in symfony (src > migrations), I droped my whole database in phpmyadmin, then in symfony, using CLI (command line interface, ctrl + รน on windows and ctrl + ` on mac (on the left of enter on the keyboard), I did :
bin/console doctrine:database:create
bin/console make:migration
bin/console doctrine:migrations:migrate
and the problem is now solved. But again, I'm really not sure this is the good way, just saying it worked for me.

Symfony2 Doctrine: redefine annotation in a inheritance class from another bundle

i have a bundle CompanyBundle with:
a class Product with this inside
/**
* #ORM\ManyToMany(targetEntity="CompanyBundle\Entity\ProductImage", inversedBy="listProduct")
**/
private $listProductImage;
and a class ProductImage having this:
/**
* #ORM\ManyToMany(targetEntity="CompanyBundle\Entity\Product", mappedBy="listProductImage")
*/
private $listProduct;
but i also extend my class Product from another ClinetBundle like that:
/**
* #ORM\Entity(repositoryClass="ClientBundle\Entity\Repository\ClientProductRepository")
*/
class ClientProduct extends Product
{
/**
* #ORM\ManyToMany(targetEntity="ClientBundle\Entity\ClientProductImage", inversedBy="listProduct")
**/
private $listProductImage;
}
but when in my controller i build a ClientProduct object class and that i try to access to the ClientProductImage from it, it is giving the class from the CompanyBundle, not the ClientBundle. so, how can i do to get the ProductImage from the ClientBundle?
it is like if my /** #ORM\ManyToMany(targetEntity="ClientBundle\Entity\ClientProductImage", inversedBy="listProduct") **/ was doing nothing :(
thanks for your help!
I finally found the solution.
I don't keep the useless relation defined in ClientProduct.
so i deleted the ManyToMany(targetEntity="ClientBundle\Entity\ClientProductImage"
and after that, i opened my app/config.yml and add the resolve_target_entities:
doctrine:
...
orm:
...
resolve_target_entities:
CompanyBundle\Entity\ProductImage: ClientBundle\Entity\ClientProductImage
all work fine, and i don't need to change the CompanyBundle :)

property dependency symfony2

Suppose I have two bundles: a FormBundle and an AccommodationBundle. These bundles I would like to Open Source independently from each other.
FormBundle i.e. has a class FormEvent
class FormEvent {
/**
* #ORM\ManyToMany(
* targetEntity="Visit",
* mappedBy="formEvents",
* cascade={"persist","remove"})
**/
protected $visits;
}
Now as you may understand, the Visit-entity is part of the AccommodationBundle.
How do I make the property visits part of the FormEvent class, only if the AccommodationBundle is registered? Do I need to extend the class FormEvent in the AccommodationBundle?

Get path of image in specified bundle

In my bundle I have a Resources/public/images/image.jpg file.
This image is accessible via http://localhost/bundles/mybundle/images/image.jpg
How can I get this /bundles/mybundle prefix from a controller?
I want to be able to generate the path to the public file without hardcoding /bundles/mybundle prefix.
I'd create a service which would do this
Create the service
The main responsibility of this class, is to get the default web path of any bundle for any resource.
As defined in assets:install command, relative paths for each bundle are expected to be /bundles/foobar/ for a given FooBarBundle
Acme\FooBundle\WebPathResolver
use Symfony\Component\HttpKernel\Bundle\BundleInterface;
class WebPathResolver
{
/**
* Gets the prefix of the asset with the given bundle
*
* #param BundleInterface $bundle Bundle to fetch in
*
* #throws \InvalidArgumentException
* #return string Prefix
*/
public function getPrefix(BundleInterface $bundle)
{
if (!is_dir($bundle->getPath().'/Resources/public')) {
throw new \InvalidArgumentException(sprintf(
'Bundle %s does not have Resources/public folder',
$bundle->getName()
));
}
return sprintf(
'/bundles/%s',
preg_replace('/bundle$/', '', strtolower($bundle->getName()))
);
}
/**
* Get path
*
* #param BundleInterface $bundle Bundle to fetch in
* #param string $type Which folder to fetch in (image, css..)
* #param string $resource Resource (image1.png)
*
* #return string Resolved path
*/
public function getPath(BundleInterface $bundle, $type, $resource)
{
$prefix = $this->getPrefix($bundle);
return sprintf('%s/%s/%s', $prefix, $type, $resource);
}
}
Declare it in your service.yml
Nothing special, but an usual service
#AcmeFooBundle/Resources/config/services.yml
services:
acme_foo.webpath_resolver:
class: Acme\FooBundle\WebPathResolver
Usage
You can then use it in your controller like this
Acme\FooBundle\Controller\BarController::bazAction
$bundle = $this->get('http_kernel')->getBundle('AcmeFooBundle');
$path = $this->get('acme.webpath_resolver')->getPath($bundle, 'image', 'foo.png');
echo $path; // Outputs /bundles/acmefoo/image/foo.png
You use assets in templates, like this:
{% image '#AcmeFooBundle/Resources/public/images/example.jpg' %}
<img src="{{ asset_url }}" alt="Example" />
{% endimage %}
or directly in src:
<img src="{{ asset('#AcmeFooBundle/Resources/public/images/example.jpg') }}" alt="Example" />
In css files you need to use relative paths.
From controller you can get full path in the same way by:
$this->container->get('templating.helper.assets')->getUrl('#AcmeFooBundle/Resources/public/images/example.jpg');
You could use something like this, but is assumes the path is the lowercased bundle name.
$controller = $request->attributes->get('_controller');
$regexp = '/(.*)\\\Bundle\\\(.*)\\\Controller\\\(.*)Controller::(.*)Action/';
preg_match($regexp, $controller, $matches);
$imagePath = '/bundles/'. strtolower($matches[2]). '/images/image.jpg';

Resources