In my Symfony2 app I have a need to provide an option to upload a text file. I've already achieved this by using the FormBuilderInterface to create a field with the 'file' type. The file gets uploaded properly but all I really need is to fetch the text from the file and insert it into the proper db table column. Is there a way to fetch the text from an uploaded file on POST? Thanks.
There are at least two ways.
Using Form Events
Using lifecycle callbacks
In both cases the main point is to open uploaded and fill its content to the entity property (after form posted but before data persisted to db). So you need to have 2 entity properties for this: uploaded file handler (not mapped to the db) and file text itself (mapped to the db).
Here is an example with the lifecycle callbacks:
Entity:
/**
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks
*/
class SomeEntity {
/**
* Virtual field used for handling the file
*
* #Assert\File()
*/
private $fileHandler;
/**
* #var string
*
* #ORM\Column(type="text")
*/
private $file;
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function saveFileContent()
{
$tmpName = md5(uniqid(mt_rand(), true)) . '.' . $this->fileHandler->guessExtension();
try
{
$this->fileHandler->move(
'../../../tmp/',
$tmpName
);
} catch (\Exception $e) {}
$this->setFile(file_get_contents('../../../tmp/' . $tmpName));
unlink('../../../tmp/' . $tmpName);
}
}
This also might be helpfull: How to handle File Uploads with Doctrine
Related
I have Cases, which also have Attachments. This is coded as a Case entity with the OneToMany association to Attachment entity. The Attachment entity has a ManyToOne association to Case entity. The code:
class Case {
/**
* #var integer
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Attachment", mappedBy="case",cascade={"persist"})
*/
protected $attachments;
class Attachment
{
/**
* #var integer
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Case", inversedBy="attachments")
* #ORM\JoinColumn(name="case_id", referencedColumnName="id")
*/
protected $case;
}
Im trying the following. I pretend to show/open the whole case into a single page. Inside the page, there will be the attachment list. At the end of that list, I pretend to put a form for new attachment submissions.
So I have written a controller to show the case, and I have created a new attachment form (AttachmentType ) and place it in the middle of the twig template, passing it into the render call of the action as a argument.
// CaseController.php
/**
* #Route("/cases/show/{case}", name="showCase", requirements={ "case" : "\d+" } )
*/
public function showCaseAction(Request $request, $case)
{
$theCase = $this->getDoctrine()->getRepository('AppBundle:Case')->findOneById( $case );
$attachment = new Attachment();
$attachment->setCase( $theCase );
$attachmentForm = $this->createForm(AttachmentType::class, $attachment);
if ( ! $theCase ) {
$this->addFlash('danger', $this->get('translator')->trans('cases.show.case_not_found', [ '%case%' => $case ] ) );
}
return $this->render('cases/caseContainer.html.twig', array( 'case' => $theCase, 'attachmentform' => $attachmentForm->createView() ) );
}
And also I have written a newAttachmentAction into the controller to perform the attachment creation.
I stop writting my code here. I dont want to condition the possible answers.
My problem is that im not able to figure out how to recover the Case object when the newAttachmentAction is called, so I can do the association. I cant figure out if i should place something ( HiddenType,EntityType,etc ) into the Attachment Form Builder to store the Case object, or maybe would be better to use some other Symfony mechanism (Services, Closure, StorageTokens). I have made a wide search along the web, but i have read some many articles, that Im stucked ! Im probably missing the right search keywords.
I hope i have made my self clear, and therefore someone can point me into the right direction to find an example or a tutorial.
Best regards and many many thanks for your time and attention !
For creating a Case, i will add a HiddenType for attachment property inside the CaseformType.
Set data_class to Attachment
When you will create the form, you will pass a new instance of Case with the attachment reference.
After the post, when you will receive the form data, you will have the linked object
I'm quite new with Doctrine, so I hope someone can help me or redirect me to the good documentation page.
I'm building an app with two entity (I reduce for explanations) :
- Tender
- File
For each tender, we can have one or more files. So I made the following objects.
Tender:
<?php
namespace TenderBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="tender")
*/
class Tender
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $tender_id;
/**
* #ORM\Column(type="array")
* #ORM\ManyToOne(targetEntity="File", inversedBy="tenders")
* #ORM\JoinColumn(name="tender_files", referencedColumnName="file_id")
*/
private $tender_files;
}
File:
<?php
namespace TenderBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
/**
* #ORM\Entity
* #ORM\Table(name="file")
*/
class File
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $file_id;
/**
* #ORM\OneToMany(targetEntity="Tender", mappedBy="tender_files", cascade={"persist", "remove"})
*/
private $file_tender;
}
First question: is it the right way to do this?
(of course, i've created the methods to get and set attributes, but they're basic).
When I persist each of my File object i'm trying to add then to my Tender instance. But to do this, I need to make $tender_files public and do this:
$tender->tender_files[]
This is not a viable solution for me because I need all my fields are private and I want to recover my object when I try to call this:
$this->getDoctrine()->getManager()->getRepository('TenderBundle:Tender')->find($id)->getTenderFiles()->getFileName();
So, I'm explaining and asking to find the right way to do what I want. I hope what i need is clear and i'm here to answers questions or show more code if needed.
Thanks!
Like Richard has mentioned, you're missing getters and setters which are declared to be public. They'll have access to your private variables. The quick way to do this with symfony:
php app/console doctrine:generate:entities
It'll generate something like this:
public function addTenderFile(\TenderBundle\Entity\File $file)
{
$this->tender_files[] = $file;
return $this;
}
/**
* Remove
*/
public function removeTenderFile(\TenderBundle\Entity\File $file)
{
$this->tender_files->removeElement($file);
}
/**
* Get
*/
public function getTenderFiles()
{
return $this->tender_files;
}
It's good practice if you're a beginner to see how your code lines up with the auto generator. Once you understand what's going on, just let the generator do the grunt work.
You should have a setter and getter in your File entity similar to this:
public function setTender(\Your\Namespace\Tender $tender)
{
$this->tender = $tender;
return $this;
}
public function setTender()
{
return $this->tender;
}
So when you instance (or create) File, you can go like so:
$file = new File(); // or file fetched from DB, etc.
// here set $file properties or whatever
$tender->setFile($file);
$entityManager->persist($tender);
$entityManager->flush();
Then your tender will be properly associated with your file.
Similarly from the File end, you should be able to do:
$file->addTender($tender);
$entityManager->persist($file);
$entityManager->flush();
And your tender will be added to your File->tenders collection.
For more information the documentation is very useful and has more or less everything you need to get started.
Also, save yourself manually creating getters and setters by using generate:doctrine:entity
This is incorrect:
/**
* #ORM\Column(type="array")
* #ORM\ManyToOne(targetEntity="File", inversedBy="tenders")
* #ORM\JoinColumn(name="tender_files", referencedColumnName="file_id")
*/
private $tender_files;
You can't persist an array to your database. A database row is one entity and it's corresponding attributes. If a tender can have many files, then this relationship should be:
* #ORM\OneToMany
Likewise for the File entity. If many files can have one Tender then it's relationship should be:
* #ORM\ManyToOne
For relationship mapping using Doctrine, it's helpful to read left-to-right with the entity YOU'RE CURRENTLY IN being on the left, and the entity you're setting as a variable being on the right.
If you're in Tender reading left-to-right Tender may have "OneToMany" files. And File(s) may have ManyToOne Tender. Doctrine Association Mapping
Is it possible to create custom metadata information in the entities? Something that would use the already present functionality of using either Annotation, yml or xml to store metadata about the entity.
Example:
/**
* #var string
*
* #ORM\Column(name="text", type="text")
* #CUSTOM\Meta(key="value") // <-- Extra information
*/
protected $text;
For what I've been researching, it seems that I should use the functionality of ClassMetadataFactory. Is it possible, or would I have to make it from scratch?
I have an entity which stores "removal requests" to either studios or models. An object (Studio or model can have many requests).
Entity RemovalRequest has a field named : object.
I would like to know if it's possible to do something like this in RemovalRequest entity:
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Studio", inversedBy="requests")
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Model", inversedBy="requests")
*/
private $object;
I can't find anything about this special case over Internet..
If it's not possible, I'm open to any suggestions you might have !
Do you realy need a new entity to store information about removal? Maybe just add a flag to Studio and Model:
/**
* #ORM\Column(name="is_to_remove", type="boolean")
*/
$isToRemove = false;
If you need RemovalRequest entity you should add two properties fore each type like this:
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Model", inversedBy="requests")
*/
$model;
/**
* #ORM\ManyToOne(targetEntity="Project\GestionBundle\Entity\Studio", inversedBy="requests")
*/
$studio;
It is bed idea to store two different classes in one property
I need to add functionality to attach a file to a comment. The upload of the image would automatically when the user drops the file into the form (in the same way that you attach a file with gmail). My question is how do I do to find the file that was previously sent to the server when the comment is submitted and to delete the comment if the document is never submitted.
Do any of you have already done something similar?
Here's the association I have between my classes Comment and Document.
class Comment extends BaseComment
{
/** ... */
/**
* #ORM\ManyToMany(targetEntity="Document", cascade={"persist","remove"})
* #ORM\JoinTable(name="fls_comment_and_documents",
* joinColumns={#ORM\JoinColumn(name="comment_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="document_id", referencedColumnName="id", unique=true)}
* )
*
* #var ArrayCollection $documents
*/
protected $documents;
/** ... */
}
Thanks in advance !