Create ordered columns in postgres with Doctrine - postgresql

I'm converting a project to Doctrine 2.4 and the used database there is PostgreSQL.
I was wondering if it's possible to tell Doctrine to create the columns of the table(entity) in a particular order?
The table I'm currently converting has a lot of columns and some of them are relations to other tables. When the table is created, the first columns would be the primary keys, followed by foreign keys and at the end - everything else.
Here is an example code:
/**
* #Entity
* #Table(name="receipts")
*/
class Receipt {
/**
* #var
* #Id
* #GeneratedValue(strategy="IDENTITY")
* #Column(name="receipt_id", type="integer")
*/
private $receipt_id;
/**
* #var
* #Column(name="created_date", type="date", nullable=false)
*/
private $created_date;
/**
* #var
* #Column(name="updated_date", type="date", nullable=true)
*/
private $updated_date;
/**
* #var
* #Column(name="invoice_number", type="string", nullable=true)
*/
private $invoice_number;
/**
* #var
* #Column(name="invoice_date", type="date", nullable=true)
*/
private $invoice_date;
/**
* #var
* #Column(name="invoice_link", type="text", nullable=true)
*/
private $invoice_link;
/**
* #var
* #ManyToOne(targetEntity="models\Receipts\Invoice\Type")
* #JoinColumn(name="invoice_type_id", referencedColumnName="invoice_type_id")
*/
private $invoiceType;
/**
* #var
* #ManyToOne(targetEntity="models\Payments\Method")
* #JoinColumn(name="invoice_payment_method_id", referencedColumnName="payment_method_id")
*/
private $invoicePaymentMethod;
/**
* #var
* #ManyToOne(targetEntity="models\Payments\Type")
* #JoinColumn(name="invoice_payment_type_id", referencedColumnName="payment_type_id")
*/
private $invoicePaymentType;
public function __construct(){
$this->created_date = new \Date_Time();
$this->updated_date = new \Date_Time();
}
?>
The code above will look like this in PHPPgAdmin:
And this is how I would like the columns to appear:
Of course, everything is working fine. It's just the people working on the project with me (including I) are used to seeing the tables & columns ordered in a concrete way so that we could find things fast and easy. Hopefully there is a Doctrine ninja here that could help me with this. I couldn't find anything in the documentation.

Related

Specific form with two enities with Symfony

I have a Category entity, this entity allow me to create a tree of my data :
I would like to create a DiscountGrid entity. A DiscountGrid is composed by a textfield name, and an array containing the discount percentage by category ($discount[idCategory] = $percentage).
In fact, I would like a form like this :
The output would be $discount[ 1 ] = 25, $discount[ 2 ] = 30,.....
I have no idea how handle this behavior with Symfony framework. Here is the declaration of my entities
class Category{
/**
* #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)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="slug", type="string", length=255, nullable=true)
*/
private $slug;
/**
* #var int
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #var int
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #var int
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #Gedmo\TreeRoot
* #ORM\ManyToOne(targetEntity="Category")
* #ORM\JoinColumn(name="root", referencedColumnName="id", onDelete="CASCADE")
*/
private $root;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent", referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
* #ORM\OrderBy({"lft" = "ASC"})
*/
private $children;
class DiscountGrid{
/**
* #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)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="slug", type="string", length=255)
*/
private $slug;
/**
* #var array
*
* #ORM\Column(name="grid", type="array")
*/
private $grid;
First of all, what you are trying to do will be a nightmare if you keep storing DiscountGrid::$grid as an array. Not only this will make your form creation extremely hard, but what will happen if someday you have to add a Category? Will you drag all the indexes to match the new Categories list? This was not your question, and I may be downvoted for suggesting that, but I definitely recommend you build your model a bit cleaner, since having a clean model is mandatory to use FormTypes properly.
What I would suggest is the following model:
DiscountGrid::$grid (rename it to $discounts) is a ManyToOne of a new entity called Discount.
Discount has an attribute $category which is a OneToMany towards Category and an attribute $reduction which is a float.
Once you have this, create a DiscountFormType with a single field reduction which is a PercentType.
Then, create another form DiscountGridFormType with a single field discounts as a CollectionType. This CollectionType should have the option entry_type set as DiscountFormType.
Finally, when you create your form in Controller, bind it a DiscountGrid entity with some Discounts in your discounts attribute. You should see a series of text boxes with percents at the end. That's the list of discounts where you can change values of the reductions.
After that, some form theming will help you display the category name next to the textbox. But I guess you already have some way to go before being at this point.

ZF2 Annotation Validators NotEmpty and Int not working?

I'm building a form in ZF2 from an entity and everything seems to work fine, except 2 of my validators are ignored for some reason. The Entity looks like this:
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255, nullable=true)
* #Annotation\Attributes({"type":"text"})
* #Annotation\Validator({"name":"NotEmpty"}) // duplicate
* #Annotation\Options({"label":"Name:"})
*/
private $name;
/**
* #var integer $sort
*
* #ORM\Column(name="sort", type="integer")
* #Annotation\Attributes({"type":"text"})
* #Annotation\Validator({"name":"Int"})
* #Annotation\Validator({"name":"NotEmpty"})
* #Annotation\Options({"label":"Sort:"})
*/
private $sort;
Yet I can submit the form without any values. I can enter a string in the SORT input, I can leave both fields empty. Why is this not working, why is there no errormessage when I leave the fields empty?
okay, it's partially because I'm STUPID.
Problem 1: notempty not working is because I defined nullable as true. Seems to override the "notempty" validator. duh.
Problem 2: I still don't know WHY that works, but if I declare the column type as string (instead of integer), suddenly the int validation works.
So here's the right code:
/**
* #var string $name
*
* #ORM\Column(name="name", type="string", length=255)
* #Annotation\Attributes({"type":"text"})
* #Annotation\Validator({"name":"NotEmpty"})
* #Annotation\Options({"label":"Name:"})
*/
private $name;
/**
* #var integer $sort
*
* #ORM\Column(name="sort", type="string")
* #Annotation\Attributes({"type":"text"})
* #Annotation\Validator({"name":"Int"})
* #Annotation\Validator({"name":"NotEmpty"})
* #Annotation\Options({"label":"Sort:"})
*/
private $sort;

Doctrine2 Association for ManyToMany Bidirectional and Insert operation for Unique OneToMany Relationships

I am using Symfony2 with Doctrine 2.
I have 5 entities in my application.
Book
Keyword (Bidirectional, should fetch all books having particular keyword)
Author (Bidirectional, should fetch all books having particular author(s))
Category (Bidirectional, should fetch all books falling into particular category)
BookExtra (Unidirectional, In Book Entity, should fetch BookExtra data)
Each book can have many keywords
Each book can have many authors
Each book can fall into a single category
Each book have exactly one BookExtra record.
The keyword and author tables should contain unique values
When a new Book is added, if the Author(s) and Keyword(s) exists, the respected Author ID and Keyword ID should be assigned to the Book, and if it doesn't exist, the new records will be created and the respected ID should be assigned to the Book.
I have the following Entity Classes:
Book.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Book
*
* #ORM\Table(name="book")
* #ORM\Entity
*/
class Book {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="isbn", type="string", length=16)
*/
protected $isbn;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
*/
protected $title;
/*
* #ORM\OneToOne(targetEntity="BookExtra")
* #ORM\JoinColumn(name="extra_id", referencedColumnName="id")
*
* ********* OR *********
* *********What should go HERE*********
*
*/
protected $bookextra;
/*
* #ORM\ManyToMany(targetEntity="Author", inversedBy="books")
* #ORM\JoinTable(name="authors_books")
*/
protected $author;
/*
* #ORM\OneToOne(targetEntity="Category")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
protected $category;
}
BookExtra.php
namespace Acme\StoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* BookExtra
*
* #ORM\Table(name="book_extra")
* #ORM\Entity
*/
class Detail {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="data", type="string", length=255)
*/
protected $data;
}
Author.php
namespace Bookhut\BookBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Author
*
* #ORM\Table(name="author")
* #ORM\Entity
*/
class Author {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100)
*/
protected $name;
// **********What should go HERE*********
protected $books;
}
Keyword & Category Entities are similar to Author Entity
The Problem is that, when i generate schema with cli, it never generates Relationships/Associations.
And what should be the proper Relationships/Associations for Book & Author Entity
I searched for this problem and proper Relationships/Associations
I found this:
Symfony2 app/console not generating properties or schema updates for Entity Relationships/Associations
Saving onetoone relation entities
doctrine2: in a one-to-many bidirectional relationship, how to save from the inverse side?
But it didn't help me.
Can somebody give an example of this type of Relationships/Associations and insert operations?
For author->books:
/**
* #ManyToMany(targetEntity="Author", inversedBy="books")
* #JoinTable(name="authors_books",
* joinColumns={#JoinColumn(name="book_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="author_id", referencedColumnName="id")}
* )
*/
protected $author;
For books->authors
/**
* #ManyToMany(targetEntity="Book", mappedBy="author")
*/
protected $books;
See http://docs.doctrine-project.org/en/latest/reference/association-mapping.html#many-to-many-bidirectional for documentation of many-to-many
See http://docs.doctrine-project.org/en/latest/reference/association-mapping.html for full documentation of associations
EDIT
Is assume all your entities will have setters and getters.
// Create new Author object
$Author = new Author();
// Use setters to set all attributes for this author
// Create new book
$Book = new Book();
// Use setters to set all attributes for book
// Attach book to author
$Author->addBook($Book);
$Book->addAuthor($Author);
The addBook and addAuthor will look like this:
// Inside author entity
public function addBook(Book $Book) {
$this->books->add($Book);
}
// Inside book entity
public function addAuthor($Author) {
$this->authors->add($Author);
}
And add a constructor to both the author and book entities:
public function __construct() {
$this->books = new ArrayCollection();
}
public function __construct() {
$this->authors = new ArrayCollection();
}
Have a look at the documentation to see more examples:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html

Trouble with Doctrine2 joined tables

I am migrating from Doctrine 1.2 to Doctrine 2.1 and am having problems with what should be a simple join
This is my first table:
/**
* Model_WBS
*
* #Table(name="WBS")
* #Entity
*/
class Model_Wbs
/**
* #var integer $id
*
* #Column(name="id", type="integer", length=8)
* #Id
* #GeneratedValue(strategy="SEQUENCE")
* #SequenceGenerator(sequenceName="wbs_seq_seq", allocationSize=1, initialValue=1)
*/
public $id;
.
.
.
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #OneToMany(targetEntity="Model_WbsFund", mappedBy="id")
* #JoinColumn(name="id", referencedColumnName="wbs_id")
*/
public $wbsfunds;
And this is my second table
/**
* Model_WbsFund
*
* #Table(name="WBS_FUNDS")
* #Entity
*/
class Model_WbsFund
/**
* #var integer $id
*
* #Column(name="id", type="integer", length=8)
* #Id
* #GeneratedValue(strategy="SEQUENCE")
* #SequenceGenerator(sequenceName="wbs_funds_seq_seq", allocationSize=1, initialValue=1)
*/
public $id;
/**
* #var integer $wbs_id
*
* #Column(name="wbs_id", type="integer", length=8)
*/
public $wbs_id;
.
.
.
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ManyToOne(targetEntity="Model_Wbs", inversedBy="wbs_id")
* #JoinColumn(name="wbs_id", referencedColumnName="id")
*/
public $WBS;
And here is my simple test
$testEntity = Model_Wbs::find(7185);
foreach($testEntity->getwbsfunds() as $wbsfundobj){
print("<br/>");
print($wbsfundobj->FUND->getFundCode()." -- ".$wbsfundobj->WBS->getWbs());
}
So I would expect to see the fund code and the wbs from the wbsfunds table that are associated with the wbs I first searched. (I put in the ->WBS->getWbs() to see if I was actually getting just the funds related to wbs id 7185).
Instead, I get all wbs_funds records. I printed out the query statement doctrine generated and it's not correct
SELECT t0.id AS ID1, t0.wbs_id AS WBS_ID2, t0.fund_id AS FUND_ID3, t0.wbs_id AS WBS_ID4, t0.fund_id AS FUND_ID5 FROM WBS_FUNDS t0
The interesting thing is that the link between wbsfundobj->FUND->geFundCode() works great, as does the wbsfundobj->WBS->getWBS(), but the link from WBS to WBS_FUNDS seems wrong.
Typically, you call find() on the repository for a entity. Something like:
// $em is your Entity Manager instance
$testEntity = $em->getRepository('\Model_Wbs')->find(7185);
// or a shorcut
$testEntity = $em->find('\Model_Wbs', 7185);
How you get a reference to the EntityManager depends upon how you bootstrapped Doctrine. If you used the Bisna application resource plugin, then it would be something like (in controller):
$em = $this->getInvokeArg('bootstrap')->getResource('doctrine')->getEntityManager();
Typically, I put this into the controller's init() method or write an action helper that encapsulates the steps above so that I could call it using something like:
$em = $this->_helper->doctrine->getEntityManager();

Zend - doctrine 2 insert data / associations mapping

i finally set up my mapping for my two tables, i can now join tables via the querybuilder..
however, i cant add data to the join column, its keeps saying null.
my account entity:
namespace Entities\Users;
/**
* #Entity(repositoryClass="\Entities\Users\Account")
* #Table(name="account")
* #HasLifecycleCallbacks
*/
class Account extends \Entities\AbstractEntity {
/**
* #Id #Column(name="accid", type="bigint",length=15)
* #GeneratedValue(strategy="AUTO")
*/
protected $accid;
/** #Column(name="name", type="string", length=255) */
protected $name;
/** #Column(name="profileid", type="integer", length=255) */
protected $profileid;
/** #Column(name="acc_added_date", type="datetime", columnDefinition="datetime", nullable=true) */
private $acc_added_date;
/**
* #ManyToOne(targetEntity="Profiledetails")
* #JoinColumn(name="profileid", referencedColumnName="pid")
*/
private $account;
and my profiledetails entity:
namespace Entities\Users;
/**
* #Entity(repositoryClass="\Entities\Users\Profiledetails")
* #Table(name="profiledetails")
* #HasLifecycleCallbacks
*/
class Profiledetails extends \Entities\AbstractEntity {
/**
* #Id #Column(name="pid", type="bigint",length=15)
* #GeneratedValue(strategy="AUTO")
*/
protected $accid;
/** #Column(name="name", type="string", length=255) */
protected $name;
/** #Column(name="profileid", type="integer", length=255) */
protected $profileid;
/** #Column(name="acc_added_date", type="datetime", columnDefinition="datetime", nullable=true) */
private $acc_added_date;
/**
* #OneToMany(targetEntity="Account", mappedBy="account")
* #JoinColumn(name="pid", referencedColumnName="pid")
*/
private $stances;
i use to use :
$postdata array ('name'=>'jason');
$entity =new \Entities\Users\Account;
$obj->setData($postdata);
$this->_doctrine->persist($obj);
$this->_doctrine->flush();
$this->_doctrine->clear();
and it doesnt add.. what the way to add data to the parent table where all linked tables get updated? because before i could enter a profileid and now its null because i used it as a the joined column..
Linked objects can be "updated" if you setup cascade=[persist] in your relationship definitions. You also need #mappedBy and #inversedBy to be set for both sides of the relations. Basically #mappedBy is set to the oneToMany side (called inverse side) and #inversedBy to the manyToOne side (called owning side)
http://www.doctrine-project.org/docs/orm/2.0/en/reference/association-mapping.html#one-to-many-bidirectional
The proper way is basically
//assume $this->_doctrine is instance of EntityManager
$user = new User();
$user->setEmail('john#example.com');
$account = new Account();
$account->setName('john');
$account->setUser($user);
$user->addAccount($account); //if no cascade set
$this->_doctrine->persist($account);
$this->_doctrine->persist($user); //if no cascade set
$this->_doctrine->flush();
http://www.doctrine-project.org/docs/orm/2.0/en/reference/working-with-associations.html#establishing-associations