Files storage with doctrine and symfony 3? - php

I need help for create my entities whit doctrine 2 in my symfony3 app :
I would like my users can posts articles which content is:
title
author
either one unique image (upload file)
or one unique movie ($url)
What do you recommend ?
Should I build my article entity like this ?
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="author", type="integer")
*/
private $author;
/**
* #var ?
*
* #ORM\Column(name="image", type="?")
*/
private $image;
/**
* #var string
*
* #ORM\Column(name="url_movie", type="string")
*/
private $url_movie;
/**
* #var integer
*
* #ORM\Column(name="media", type="integer")
*/
private $media;
}
(in controller : if $media = 1 => this is an image, else this is a video)
Or use something like Relation One-To-One with a new entity "media" for example ?
What is the best way for my case ?

Yes, it's normal. Sadly, the discriminator column is meant to be used by Doctrine, database side, therefore it's not accessible in your entity. There's two possible way to achieve what you want:
The first, using the children class name:
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
//...
/**
* Get article type.
*
* #return string
*/
public function getType()
{
// This will return "movie" or "image"
return strtolower(substr(strrchr(get_class($this), "\\"), 1));
}
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
//getter setter
}
The second, by declaring manually the type in your class:
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #var string
*/
protected $type;
//...
/**
* Get article type.
*
* #return string
*/
public function getType()
{
return $this->type;
}
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
public function __construct()
{
$this->type = 'movie';
}
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
public function __construct()
{
$this->type = 'image';
}
//getter setter
}
Personnaly, I have a preference for the first solution. I find it cleaner, and more evolutive (this code will adapt if you have to add a third article type).
Of course, you can also use instanceof to determine which subclass is the Article entity you're manipulating.

I think using a Media entity and handling the media type in the Media entity is the best way.
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ...
/**
* #var Media
*
* #OneToOne(targetEntity="Media")
* #ORM\JoinColumn(name="media_id", referencedColumnName="id")
*/
private $media;
}
class Media
{
const TYPE_IMAGE = 'image';
const TYPE_MOVIE = 'movie';
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $url;
/**
* #var string
*
* #ORM\Column(type="string")
*/
private $type;
}
An other way to do it could be to use entity inheritance to have differents entities for images and movies - if you need to.

Ok I did a Single Table Inheritance on my article class :
/**
* Article
*
* #ORM\Table(name="article")
* #ORM\Entity(repositoryClass="PM\PlatformBundle\Repository\ArticleRepository")
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="media", type="string")
* #ORM\DiscriminatorMap({"article" = "Article", "movie" = "Movie", "image" = "Image"})
*/
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
//...
}
/**
* Movie
*
* #ORM\Entity
*/
class Movie extends Article
{
/**
* #var string
*
* #ORM\Column(name="url", type="text")
*/
private $url;
//getter setter
}
/**
* Image
*
* #ORM\Entity
*/
class Image extends Article
{
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255)
*/
private $path;
//getter setter
}
That works great !

Related

Table join with entity (Symfony)

I'm new to Symfony and am having trouble getting my entities set up. I want to be able to access the tag names from my Acasset entity.
Here are the relevant entities:
class Actag
{
/**
* #var string
*
* #ORM\Column(name="tag_name", type="string", length=200, nullable=true)
*/
private $tagName;
/**
* #var integer
*
* #ORM\Column(name="tag_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $tagId;
/**
* Acassettag
*
* #ORM\Table(name="acAssetTag", indexes={#ORM\Index(name="IDX_7C4A2A745DA1941", columns={"asset_id"}), #ORM\Index(name="IDX_7C4A2A74BAD26311", columns={"tag_id"})})
* #ORM\Entity
*/
class Acassettag
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \AdminBundle\Entity\Acasset
*
* #ORM\ManyToOne(targetEntity="AdminBundle\Entity\Acasset", inversedBy="asset", cascade="PERSIST")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="asset_id", referencedColumnName="asset_id")
* })
*/
private $asset;
/**
* #var \AdminBundle\Entity\Actag
*
* #ORM\ManyToOne(targetEntity="AdminBundle\Entity\Actag")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="tag_id", referencedColumnName="tag_id")
* })
*/
private $tag;
/**
* Acasset
*
* #ORM\Table(name="acAsset", indexes={#ORM\Index(name="IDX_3B81679E68BA92E1", columns={"asset_type"}), #ORM\Index(name="IDX_3B81679E12469DE2", columns={"category_id"})})
* #ORM\Entity(repositoryClass="AdminBundle\Repository\AcAssetRepository")
*/
class Acasset
{
/**
* #var string
*
* #ORM\Column(name="asset_name", type="string", length=100, nullable=false)
*/
private $assetName;
/**
* #var integer
*
* #ORM\Column(name="asset_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $assetId;
/**
* #var \AdminBundle\Entity\Acassettype
*
* #ORM\OneToOne(targetEntity="AdminBundle\Entity\Acassettype", mappedBy="asset", fetch="EAGER", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="asset_type_id", referencedColumnName="asset_type_id")
* })
*/
private $assetType;
/**
* #var \AdminBundle\Entity\Actag
*
* #ORM\ManyToOne(targetEntity="AdminBundle\Entity\Actag")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="tag_id", referencedColumnName="tag_id")
* })
*/
private $assetTags;
/**
* Set tag
*
* #param \AdminBundle\Entity\Actag $tag
*
* #return Acassettag
*/
public function setAssetTags(\AdminBundle\Entity\Actag $tag = null)
{
$this->tag = $tag;
return $this;
}
/**
* Get tag
*
* #return \AdminBundle\Entity\Actag
*/
public function getAssetTags()
{
return $this->tag;
}
So in my Acasset entity I have created $assetTags but I am getting an error: Invalid column name 'tag_id'.
But $tag in the Acasettag entity works, which is set up the same way. What am I missing, still struggling a little with this part of Symfony.
I didnt't include all the getters and setters in this post, just the one I created for this.
Why does your Actag-Entity hat also a tagId field? You don't need a defined Id-Column on the Owning-Side. You have to define a OneToMany.
Many Acassettag are owned by one Actag (because of the ManyToOne-Definition in Acassettag
So every Acassettag will need the field actag_id with the Id of the owning Actag which will be autogenerated by your ManyToOne-Definition
When you create a new Acassettag Entity and want to "connect" it with a existing Actag you need Acassettag
public setActag(Actag $actag)
{
$this->tag = $actag;
return $this;
}

ongr-io elasticsearch bundle implementation with doctrine symfony2

In our symfony2 project we want to implement elasticsearch with ongr-io bundle, but our whole system is built on doctrine. Is it possible to somehow use ongr-io elasticsearch bundle without totally redoing whole database with documents instead of entities?
Entity that I want to index (fields for search would be: name, ChildCategory and ParentCategory):
/**
* Course
*
* #ORM\Table(name="course")
* #ORM\Entity(repositoryClass="CoreBundle\Repository\CourseRepository")
* #ORM\HasLifecycleCallbacks
*/
class Course
{
/**
* #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=false)
*/
private $name;
/**
* #var ParentCategory
*
* #ORM\ManyToOne(targetEntity="ParentCategory")
* #ORM\JoinColumn(name="parentCategory_id", referencedColumnName="id")
*/
private $parentCategory;
/**
* #var ChildCategory
*
* #ORM\ManyToOne(targetEntity="ChildCategory", inversedBy="courses")
* #ORM\JoinColumn(name="childCategory_id", referencedColumnName="id")
*/
private $childCategory;
/**
* #var City
*
* #ORM\ManyToOne(targetEntity="City")
* #ORM\JoinColumn(name="city_id", referencedColumnName="id")
*/
private $city;
/**
* #var Users
*
* #ORM\ManyToOne(targetEntity="UserBundle\Entity\Users", inversedBy="course")
* #ORM\JoinColumn(name="owner_id", referencedColumnName="id")
*/
private $owner;
/**
* #var text
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #var Picture
*
* #ORM\OneToMany(targetEntity="CoreBundle\Entity\Picture", mappedBy="course", cascade={"persist", "remove"})
*/
private $pictures;
/**
* #var Ratings
*
* #ORM\OneToMany(targetEntity="CoreBundle\Entity\Ratings", mappedBy="courseid")
*/
private $ratings;
public $avgRating;
}
it's very interesting.
The first thing is Entity location. The document for ElasticsearchBundle has to be in Document directory. It's not a big deal. All you need to do is to create an empty class and extend entity with #Document annotation. Next are the fields. With name field there is no problem at all, just add #property annotation and you are good. More interesting is with relations. My suggestion would be to create separate property and in the getter return value from the relation for that field. I hope you got the idea.
Update:
Here's an example of documents:
AppBundle/Entity/Post
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use ONGR\ElasticsearchBundle\Annotation as ES;
/**
* Post
*
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
*/
class Post
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ES\Property(type="string")
* #ORM\Column(name="title", type="string", length=255, nullable=true)
*/
private $title;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #var string
*
* #ES\Property(name="user", type="string")
*/
private $esUser;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set user
*
* #param \AppBundle\Entity\User $user
*
* #return Post
*/
public function setUser(\AppBundle\Entity\User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* #return string
*/
public function getEsUser()
{
return $this->esUser;
}
/**
* #param string $esUser
*/
public function setEsUser($esUser)
{
$this->esUser = $esUser;
}
}
AppBundle/Document/Post
<?php
namespace AppBundle\Document;
use ONGR\ElasticsearchBundle\Annotation as ES;
use AppBundle\Entity\Post as OldPost;
/**
* #ES\Document()
*/
class Post extends OldPost
{
}

Can't Create association on Symfony2

I'm trying to create a "OneToMany" bidirectional association in my project but when I execute "doctrine:schema:update" nothing happens.
If I create this association directly from Sequel Pro and run the update schema command, that changes dissapear... :/
The relations is:
- One "id" from Customers Table with many "customer_id" form Control table.
Here is the Customers code:
<?php
namespace Ourentec\CustomersBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Customers
*
* #ORM\Table()
* #ORM\Entity
*/
class Customers
{
/* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="lastname", type="string", length=100)
*/
private $lastname;
/**
* #var string
*
* #ORM\Column(name="address", type="text")
*/
private $address;
/**
* #var string
*
* #ORM\Column(name="phone", type="string", length=100)
*/
private $phone;
/**
* #var string
*
* #ORM\Column(name="pass", type="string", length=100)
*/
private $pass;
/**
* #var string
*
* #ORM\Column(name="tasks", type="text")
*/
private $tasks;
/**
* #var string
*
* #ORM\Column(name="status", type="string", length=100)
*/
private $status;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=100)
*/
private $email;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var string
*
* #ORM\Column(name="location", type="string", length=100)
*/
private $location;
/**
* #ORM\OneToMany(targetEntity="Control", mappedBy="customers")
*/
private $customer_id;
public function __construct()
{
$this->customer_id = new ArrayCollection();
}
And the Control code:
<?php
namespace Ourentec\CustomersBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Control
*
* #ORM\Table()
* #ORM\Entity
*/
class Control
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="customer_id", type="integer")
*
* #ORM\ManyToOne(targetEntity="Customers", inversedBy="control")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
*/
private $customerId;
/**
* #var integer
*
* #ORM\Column(name="user_id", type="integer")
*/
private $userId;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var integer
*
* #ORM\Column(name="seen", type="smallint")
*/
private $seen;
I followed the documentation from this 2 websites
http://symfony.com/doc/current/book/doctrine.html
http://librosweb.es/libro/symfony_2_x/capitulo_8/relaciones_y_asociaciones_de_entidades.html
But I don't know why it does not work..
Any idea will be appreciated :)
Mapping are not correct, I will try to explain how it works.
In Customers entity (you should rename it to Customer, entites names are singular)
/**
* #ORM\OneToMany(targetEntity="Control", mappedBy="customer")
*/
private $controls;
Mapped by option defines field name in the other entity.
/**
* #var integer
*
* #ORM\Column(name="customer_id", type="integer")
*
* #ORM\ManyToOne(targetEntity="Customers", inversedBy="controls")
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
*/
private $customer;
Same thing with inversedBy.
In Customers entity you also need to init controls var as an ArrayCollection:
public function __construct()
{
$this->controls = new ArrayCollection();
}
With these mappings schema should be updated correctly.
For more info, check doctrine docs.

Symfony 2 relations issue

I nead help.
I have 3 entities. Book, Category And BookCategory - book can have multiple categories so i used another table.
I can easily acces Book and Category useing BookCategory table but i dont know how to do this by Book->BookCategory->Category.
class Category
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="text")
*/
private $name;
/**
* #var Category
* #ORM\ManyToOne(targetEntity="Category", inversedBy="Category")
* #ORM\JoinColumn(name="parent", referencedColumnName="id")
*/
private $parent;
class BookCategory
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Book
* #ORM\ManyToOne(targetEntity="Book", inversedBy="BookCategory")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $bookId;
/**
* #var Category
* #ORM\ManyToOne(targetEntity="Category", inversedBy="BookCategory")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $categoryId;
/**
* #var integer
*
* #ORM\Column(name="priority", type="integer")
*/
private $priority;
class Book
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="text")
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="author", type="text")
*/
private $author;
/**
* #var float
*
* #ORM\Column(name="price", type="float")
*/
private $price;
How i need to config my entities or how to make my DQL to achive wanted results?
With your code, you only established the relationship from BookCategory to Book. As you said, that enables you to get the Book associated to one BookCategory.
To go the other way and get all BookCategory that belong to one book, you also need to specify this relationship. What you want is a OneToMany relationship from Book to BookCategory.
<?php
//...
use Doctrine\ORM\Mapping\OneToMany;
class Book
{
//...
/**
* #OneToMany(targetEntity="BookCategory", mappedBy="bookId")
*/
private $bookCategories;
//...
}
class BookCategory
{
//...
/**
* #var Book
* #ORM\ManyToOne(targetEntity="Book", inversedBy="bookCategories")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
private $bookId;
//...
}
After adding the necessary getters and setters, getBookCategories() will give you an Array with all BookCategory that belong to the Book.
For more details, have a look at the official Symfony2 documentation:
http://symfony.com/doc/current/book/doctrine.html#relationship-mapping-metadata
Edit:
Included use statement. Corrected inversedBy property for bookId.

Doctrine ORM does not persist relations

I have a problem persisting relations with doctrine.
This is the process I'm doing:
Create Material object & fill with data
Create PossibleMaterialConfiguration Object & fill with data
Assign PossibleMaterialConfiguration Object to Material Object with
$material->addPossibleMaterialConfiguration($possibleMaterialConfiguration);
Result:
PossibleMaterialConfiguration.material_id is empty!
But when I do it the other way round and assign Material Object to PossibleMaterialConfiguration it does work!
I'm driving crazy! What the hell is going on here? Thanks in advance.
My two entities:
Material.php
/**
* Material
*
* #ORM\Table()
* #ORM\Entity
*/
class Material
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #ORM\OneToMany(targetEntity="PossibleMaterialConfiguration", mappedBy="material", cascade={"persist"})
*/
private $possibleMaterialConfigurations;
PossibleMaterialConfiguration.php
/**
* PossibleMaterialConfiguration
*
* #ORM\Table()
* #ORM\Entity
*/
class PossibleMaterialConfiguration
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Material", inversedBy="possibleMaterialConfigurations")
* #ORM\JoinColumn(name="material_id", referencedColumnName="id")
*/
private $material;
/**
* #var float
*
* #ORM\Column(name="thickness", type="decimal")
*/
private $thickness;
/**
* #var float
*
* #ORM\Column(name="lengthMin", type="decimal")
*/
private $lengthMin;
/**
* #var float
*
* #ORM\Column(name="lengthMax", type="decimal")
*/
private $lengthMax;
/**
* #var float
*
* #ORM\Column(name="widthMin", type="decimal")
*/
private $widthMin;
/**
* #var float
*
* #ORM\Column(name="widthMax", type="decimal")
*/
private $widthMax;
Take a look at the article about owing side in doctrine :
http://docs.doctrine-project.org/en/latest/reference/unitofwork-associations.html
Basically, doctrine won't check the side where is the "mappedBy" annotation.
If you want to do :
$material->addPossibleMaterialConfiguration($possibleMaterialConfiguration);
Then your addPossibleMaterialConfiguration function should be :
function addPossibleMaterialConfiguration($assoc) {
$this->possibleMaterialConfigurations[] = $assoc;
$assoc->setMaterial($this);
}

Categories