Doctrine overwrite column value for composite key - doctrine

I need a relation uniderect one-to-one through composite key. I am using PostgreSQL.
#[Entity()]
#[Table(name: "passengers")]
class Passenger
{
#[Id]
#[Column(type: "uuid")]
private string $id;
#[Id]
#[Column(type: "uuid")]
private string $projectId;
#[OneToOne(targetEntity: "Ticket", cascade: ["persist"])]
#[JoinColumn(name: "ticket_id", referencedColumnName: "id")]
#[JoinColumn(name: "project_id", referencedColumnName: "project_id")]
private ?Ticket $ticket;
/**
* #return string
*/
public function getId(): string
{
return $this->id;
}
/**
* #param string $id
*/
public function setId(string $id): void
{
$this->id = $id;
}
/**
* #return string
*/
public function getProjectId(): string
{
return $this->projectId;
}
/**
* #param string $projectId
*/
public function setProjectId(string $projectId): void
{
$this->projectId = $projectId;
}
/**
* #return Ticket|null
*/
public function getTicket(): ?Ticket
{
return $this->ticket;
}
/**
* #param Ticket|null $ticket
*/
public function setTicket(?Ticket $ticket): void
{
$this->ticket = $ticket;
}
}
#[Entity()]
#[Table(name: "tickets")]
class Ticket
{
#[Id]
#[Column(type: Types::STRING)]
private string $id;
#[Id]
#[Column(type: "uuid")]
private string $projectId;
/**
* #return string
*/
public function getId(): string
{
return $this->id;
}
/**
* #param string $id
*/
public function setId(string $id): void
{
$this->id = $id;
}
/**
* #return string
*/
public function getProjectId(): string
{
return $this->projectId;
}
/**
* #param string $projectId
*/
public function setProjectId(string $projectId): void
{
$this->projectId = $projectId;
}
}
I'm trying to persist Passenger entity without Ticket entity:
$passenger = new Passenger();
$passenger->setId('07f5f7c8-def7-4fad-a146-b573421d8832');
$passenger->setProjectId('48ae51a1-4405-4f1d-8061-55a732d383a1');
$this->entityManager->persist($passenger);
$this->entityManager->flush();
I get an error:
Doctrine\DBAL\Exception\NotNullConstraintViolationException : An exception occurred while executing 'INSERT INTO passengers (id, project_id, ticket_id) VALUES (?, ?, ?)' with params ["07f5f7c8-def7-4fad-a146-b573421d8832", null, null]:
SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column "project_id" of relation "passengers" violates not-null constraint
DETAIL: Failing row contains (07f5f7c8-def7-4fad-a146-b573421d8832, null, null).
I want to draw your attention to the fact that doctrine in sql overwrite the value of project_id for composite key to null.
If I persist Passenger by specifying the Ticket, everything works without errors.
Is this a doctrine bug or am I doing something wrong?

Related

Laravel: Stop using UUID as foreign key

Laravel is trying to use uuid field as foreign key. And I want to use foreign key with the field id. Is there any option there?
Using this trait on Model. And then it is trying to use the uuid as foreign key. But still I want to use id as foreign key.
<?php
namespace App\Library;
use Ramsey\Uuid\Uuid;
trait UsesUuid
{
/**
* #return string
*/
public function getKeyName()
{
return 'uuid';
}
/**
* #return string
*/
public function getKeyType()
{
return 'string';
}
/**
* #return false
*/
public function getIncrementing()
{
return false;
}
/**
* #param $query
* #param $uuid
* #return mixed
*/
public function scopeUuid($query, $uuid)
{
return $query->where($this->getUuidName(), $uuid);
}
/**
* #return string
*/
public function getUuidName()
{
return property_exists($this, 'uuidName') ? $this->uuidName : 'uuid';
}
/**
* #return string
*/
public function getRouteKeyName()
{
return property_exists($this, 'uuidName') ? $this->uuidName : 'uuid';
}
/**
*
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->{$model->getUuidName()} = Uuid::uuid4()->toString();
});
}
}
There is nothing special about this trait. You can make your own trait with id instead of uuid and everything will work fine.
The issue came from methods getIncrementing() and getKeyName(). Laravel calls getKeyName() in amount of built-in functions to interacts with relationships, also other actions like delete(), route bindings, etc.
You should allow any models which uses this trait to custom the Primary key (PK), so that uuid is only common column at all.
Your trait definition is force PK column as uuid.
Below is my recommended for the trait
<?php
namespace App\Library;
use Ramsey\Uuid\Uuid;
trait UsesUuid
{
/* Override this method to set `uuid` as PK */
public function isUuidAsPrimaryKey()
{
return false;
}
/**
* #return string
*/
public function getKeyName()
{
return $this->isUuidAsPrimaryKey() ? $this->getUuidName() : $this->primaryKey;
}
/**
* #return string
*/
public function getKeyType()
{
return $this->isUuidAsPrimaryKey() ? 'string' : $this->keyType;
}
/**
* #return false
*/
public function getIncrementing()
{
return !$this->isUuidAsPrimaryKey();
}
/**
* #param $query
* #param $uuid
* #return mixed
*/
public function scopeUuid($query, $uuid)
{
return $query->where($this->getUuidName(), $uuid);
}
/**
* #return string
*/
public function getUuidName()
{
return property_exists($this, 'uuidName') ? $this->uuidName : 'uuid';
}
/**
* #return string
*/
public function getRouteKeyName()
{
return property_exists($this, 'uuidName') ? $this->uuidName : 'uuid';
}
/**
*
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->{$model->getUuidName()} = Uuid::uuid4()->toString();
});
}
}
If a model need to have uuid as PK, for example Book model
class Book extends Model
{
use UsesUuid;
public function isUuidAsPrimaryKey()
{
return true;
}
}
Please recheck the method isUuidAsPrimaryKey. If it may not be overridden (due to conflict), then use a property instead.

Doctrine 2: cascade persist Oracle "IDENTITY" is returning 0 as last inserted ID

I am using doctrine 2 with oracle, the tables in the database has some triggers that generate the IDs, and my ID mapping of my tables is like the following:
/**
* #orm\Id
* #orm\Column(type="integer");
* #orm\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
and I have a OneToMany relation, with cascade={"persist"} but it is not working, I tried the same code with MySQL and it is working fine, but in oracle the last insert Id seems to always return 0 instead of the real id of the inserted row... and so the cascade persist is not working... is this a bug in doctrine or am I doing something wrong? any help?
After following the code it seems that the method
Doctrine\ORM\Id\IdentityGenerator::generate
is returning 0, I don't know why it is being invoked since the sequenceName is null (there is no sequence in the deffinition!
EDIT: Here are the entities:
The Client Entity:
/**
* #ORM\Entity
* #ORM\Table(name="clients")
**/
class Client {
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\Column(type="integer")
*/
protected $id;
/** #ORM\Column(name="name",type="string",length=255,unique=true) */
protected $name;
/**
* #ORM\OneToMany(targetEntity="ContactInformation", mappedBy="client", cascade={"persist"})
**/
protected $contactInformations;
public function __construct() {
$this->contactInformations = new ArrayCollection();
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getContactInformations() {
return $this->contactInformations;
}
public function addContactInformations(Collection $contactInformations)
{
foreach ($contactInformations as $contactInformation) {
$contactInformation->setClient($this);
$this->contactInformations->add($contactInformation);
}
}
/**
* #param Collection $tags
*/
public function removeContactInformations(Collection $contactInformations)
{
foreach ($contactInformations as $contactInformation) {
$contactInformation->setClient(null);
$this->contactInformations->removeElement($contactInformation);
}
}
public function setContactInformations($contactInformations) {
$this->contactInformations = $contactInformations;
return $this;
}
}
The Contact Information Entity:
/**
* #ORM\Entity
* #ORM\Table(name="contact_informations")
**/
class ContactInformation {
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="ContactInformationType")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id")
**/
protected $type;
/** #ORM\Column(type="text") */
protected $value;
/**
* #ORM\ManyToOne(targetEntity="Client", inversedBy="contact_informations")
* #ORM\JoinColumn(name="client_id", referencedColumnName="id")
**/
private $client;
public function getId() {
return $this->id;
}
public function getType() {
return $this->type;
}
public function setType($type) {
$this->type = $type;
return $this;
}
public function getValue() {
return $this->value;
}
public function setValue($value) {
$this->value = $value;
return $this;
}
public function getClient() {
return $this->client;
}
public function setClient($client = null) {
$this->client = $client;
return $this;
}
}
Oracle doesn't support auto incrementing, so you cannot use the "IDENTITY" strategy in Doctrine. You'll have to use the "SEQUENCE" (or "AUTO") strategy.
When specifying "AUTO", Doctrine will use "IDENTITY" for MySql and "SEQUENCE" for Oracle.
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/basic-mapping.html#identifier-generation-strategies

oneToOne issue with Doctrine

I have 2 tables and I am trying to create a OneToOne assocation with doctrine using the zend framework, my error is as follows, table structure and entity code including controller code can be found below, any idea why I am getting this error?
Fatal error: Call to undefined method Closure::getDate() in C:\htdocs\ea2\module\Easchnitstelle\view\easchnitstelle\transactions\index.phtml on line 132
Table Structure
CREATE TABLE `transaction` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`file_id` int(10) unsigned NOT NULL,
`meta_data_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10386 DEFAULT CHARSET=utf8$$
CREATE TABLE `transaction_meta_data` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`meta_data_sender_id` int(10) unsigned NOT NULL,
`sender_account` float NOT NULL,
`sum_amounts` float NOT NULL,
`count` smallint(5) unsigned NOT NULL,
`date` date DEFAULT NULL,
`sum_bankcodes` float NOT NULL,
`sum_accounts` float NOT NULL,
`type` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10386 DEFAULT CHARSET=utf8$$
Here are my two entities:
namespace Easchnitstelle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Entity Class representing a Post of our Zend Framework 2 Blogging Application
*
* #ORM\Entity
* #ORM\Table(name="transaction")
* #property int $id
* #property string $file_id
* #property string $meta_data_id
*/
class Transaction
{
/**
* #ORM\Id #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/** #ORM\Column(type="integer") */
protected $file_id;
/** #ORM\Column(type="integer") */
protected $meta_data_id;
/**
* #ORM\OneToMany(targetEntity="TransactionData", mappedBy="transaction")
* #ORM\JoinColumn(name="transaction_id", referencedColumnName="id")
**/
private $TransactionData;
/**
* #ORM\OneToOne(targetEntity="TransactionMetaData", mappedBy="transaction")
* #ORM\JoinColumn(name="meta_data_id", referencedColumnName="id")
**/
private $TransactionMetaData;
public function __construct() {
$this->TransactionData = new ArrayCollection();
$this->TransactionMetaData = new ArrayCollection();
}
public function getTransactionData() {
return $this->TransactionData;
}
public function setTransactionData($TransactionData){
$this->TransactionData = $TransactionData;
return $this;
}
public function getTransactionMetaData() {
return $this->TransactionMetaData;
}
public function setTransactionMetaData($TransactionMetaData){
$this->TransactionMetaData = $TransactionMetaData;
return $this;
}
public function setId($id){
$this->id = $id;
return $this;
}
public function getId(){
return $this->id;
}
public function setFileId($file_id){
$this->file_id = $file_id;
return $this;
}
public function getFileId(){
return $this->file_id;
}
public function setMetaDataId($meta_data_id){
$this->meta_data_id = $meta_data_id;
return $this;
}
public function getMetaDataId(){
return $this->meta_data_id;
}
}
And:
<?php
namespace Easchnitstelle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* This class is somewhere in your library
* #ORM\Entity
* #ORM\Table(name="transaction_meta_data")
*/
class TransactionMetaData {
/**
* #ORM\Id #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/** #ORM\Column(type="integer") */
protected $meta_data_sender_id;
/** #ORM\Column(type="float") */
protected $sum_amounts;
/** #ORM\Column(type="float") */
protected $count;
/** #ORM\Column(type="date") */
protected $date;
/** #ORM\Column(type="float") */
protected $sum_bankcodes;
/** #ORM\Column(type="float") */
protected $sum_accounts;
/** #ORM\Column(type="integer") */
protected $type;
/**
* #ORM\OneToOne(targetEntity="Transaction", mappedBy="TransactionMetaData")
* #ORM\JoinColumn(name="meta_data_id", referencedColumnName="id")
**/
protected $transaction;
public function setId($id){
$this->id = $id;
return $this;
}
public function getId(){
return $this->id;
}
public function getSumAmounts() {
return $this->sum_amounts;
}
public function setSumAmounts($sum_amounts) {
$this->sum_amounts = $sum_amounts;
}
public function getCount() {
return $this->count;
}
public function setCount($count) {
$this->count = $count;
}
public function getDate() {
return $this->date;
}
public function setDate($date) {
$this->date = $date;
}
public function getSumBankcodes() {
return $this->sum_bankcodes;
}
public function setSumBankcodes($sum_bankcodes) {
$this->sum_bankcodes = $sum_bankcodes;
}
public function getSumAccounts() {
return $this->sum_accounts;
}
public function setSumAccounts($sum_accounts) {
$this->sum_accounts = $sum_accounts;
}
public function getType() {
return $this->type;
}
public function setType($type) {
$this->type = $type;
}
}
Below is the code in my view:
foreach($Transactions as $t){
$amount = '';
foreach($t->getTransactionData() as $t_data){
//echo $amount = $t_data->getPurposes();
}
//die;
//echo get_class($t->getTransactionData());
//$tm_data = $t->getTransactionMetaData();
foreach($t->getTransactionMetaData() as $tm_data){
//echo get_class($t->getTransactionMetaData());
$date = $tm_data->getDate();
echo $date;
die;
}
$data[] = array($amount);
}
The relation is a 1:1, not a 1:n. Why do you use the foreach keyword?
As I see your code, it should simply look like this:
foreach($Transactions as $transaction) {
$metadata = $transaction->getTransactionMetaData();
echo $metadata->getDate();
}
There is no foreach required with getTransactionMetaData()

Jedis and JOhm Error

I am using Redis installed on Windows via chocolatey and setup jedis and JOhm in java project. The Redis server is live Redis version 2.6 When I want to save a Java object like the one in the post I got an error message.
java.lang.NoSuchMethodError: redis.clients.jedis.Jedis.sadd(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Long;
at redis.clients.johm.Nest.sadd(Nest.java:168)
at redis.clients.johm.JOhm.save(JOhm.java:220)
at redis.clients.johm.JOhm.save(JOhm.java:146)
This is my java object:
/**
*
*/
package com.smsgh.unitysmpp.MessageProcessor;
import java.io.Serializable;
import org.joda.time.DateTime;
import redis.clients.johm.Attribute;
import redis.clients.johm.Id;
import redis.clients.johm.Model;
/**
* #author Arsene Tochemey GANDOTE This class holds the Messages that needs a
* Delivery Receipt
*/
#Model
public class StoredShortMessage implements Serializable {
/**
*
*/
private static final long serialVersionUID = 6185862961624213864L;
#Id
private Integer id;
// session Id
#Attribute
private Long smppSessionId;
// Message Id
#Attribute
private String messageId;
// ESME Account Number#
#Attribute
private Long accountNumber;
// ESME Account Id
#Attribute
private String accountId;
// ESME API Pub Key
#Attribute
private String apiPublicKey;
// Message state
#Attribute
private String messageState;
// Network Error
#Attribute
private String networkErrorCode;
// First 20 Characters of the message
#Attribute
private String mesgFirstLines;
// esme TCP/IP connection
#Attribute
private String ip;
// message submitted datetime
#Attribute
private DateTime submitDate;
// final state date
#Attribute
private DateTime doneDate;
// source address
#Attribute
private byte srcTon;
#Attribute
private byte srcNpi;
#Attribute
private String srcAddr;
// destination address
#Attribute
private byte destTon;
#Attribute
private byte destNpi;
#Attribute
private String destAddr;
// delivery state
#Attribute
private char dlrState;
/**
*
*/
public StoredShortMessage() {
}
/**
* #return the smppSessionId
*/
public Long getSmppSessionId() {
return smppSessionId;
}
/**
* #param smppSessionId
* the smppSessionId to set
*/
public void setSmppSessionId(Long smppSessionId) {
this.smppSessionId = smppSessionId;
}
/**
* #return the messageId
*/
public String getMessageId() {
return messageId;
}
/**
* #param messageId
* the messageId to set
*/
public void setMessageId(String messageId) {
this.messageId = messageId;
}
/**
* #return the accountNumber
*/
public Long getAccountNumber() {
return accountNumber;
}
/**
* #param accountNumber
* the accountNumber to set
*/
public void setAccountNumber(Long accountNumber) {
this.accountNumber = accountNumber;
}
/**
* #return the accountId
*/
public String getAccountId() {
return accountId;
}
/**
* #param accountId
* the accountId to set
*/
public void setAccountId(String accountId) {
this.accountId = accountId;
}
/**
* #return the apiPublicKey
*/
public String getApiPublicKey() {
return apiPublicKey;
}
/**
* #param apiPublicKey
* the apiPublicKey to set
*/
public void setApiPublicKey(String apiPublicKey) {
this.apiPublicKey = apiPublicKey;
}
/**
* #return the messageState
*/
public String getMessageState() {
return messageState;
}
/**
* #param messageState
* the messageState to set
*/
public void setMessageState(String messageState) {
this.messageState = messageState;
}
/**
* #return the networkErrorCode
*/
public String getNetworkErrorCode() {
return networkErrorCode;
}
/**
* #param networkErrorCode
* the networkErrorCode to set
*/
public void setNetworkErrorCode(String networkErrorCode) {
this.networkErrorCode = networkErrorCode;
}
/**
* #return the mesgFirstLines
*/
public String getMesgFirstLines() {
return mesgFirstLines;
}
/**
* #param mesgFirstLines
* the mesgFirstLines to set
*/
public void setMesgFirstLines(String mesgFirstLines) {
this.mesgFirstLines = mesgFirstLines;
}
/**
* #return the ip
*/
public String getIp() {
return ip;
}
/**
* #param ip
* the ip to set
*/
public void setIp(String ip) {
this.ip = ip;
}
/**
* #return the submitDate
*/
public DateTime getSubmitDate() {
return submitDate;
}
/**
* #param submitDate
* the submitDate to set
*/
public void setSubmitDate(DateTime submitDate) {
this.submitDate = submitDate;
}
/**
* #return the doneDate
*/
public DateTime getDoneDate() {
return doneDate;
}
/**
* #param doneDate
* the doneDate to set
*/
public void setDoneDate(DateTime doneDate) {
this.doneDate = doneDate;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
/**
* #return the srcTon
*/
public byte getSrcTon() {
return srcTon;
}
/**
* #param srcTon
* the srcTon to set
*/
public void setSrcTon(byte srcTon) {
this.srcTon = srcTon;
}
/**
* #return the srcNpi
*/
public byte getSrcNpi() {
return srcNpi;
}
/**
* #param srcNpi
* the srcNpi to set
*/
public void setSrcNpi(byte srcNpi) {
this.srcNpi = srcNpi;
}
/**
* #return the srcAddr
*/
public String getSrcAddr() {
return srcAddr;
}
/**
* #param srcAddr
* the srcAddr to set
*/
public void setSrcAddr(String srcAddr) {
this.srcAddr = srcAddr;
}
/**
* #return the destTon
*/
public byte getDestTon() {
return destTon;
}
/**
* #param destTon
* the destTon to set
*/
public void setDestTon(byte destTon) {
this.destTon = destTon;
}
/**
* #return the destNpi
*/
public byte getDestNpi() {
return destNpi;
}
/**
* #param destNpi
* the destNpi to set
*/
public void setDestNpi(byte destNpi) {
this.destNpi = destNpi;
}
/**
* #return the destAddr
*/
public String getDestAddr() {
return destAddr;
}
/**
* #param destAddr
* the destAddr to set
*/
public void setDestAddr(String destAddr) {
this.destAddr = destAddr;
}
/**
* #return the dlrState
*/
public char getDlrState() {
return dlrState;
}
/**
* #param dlrState
* the dlrState to set
*/
public void setDlrState(char dlrState) {
this.dlrState = dlrState;
}
}
Can someone tell me what can be the error? Thanks
To solve I have to download the source code and use it. I think there is a bug in the jar file I downloaded.
Download Project Source from
https://github.com/xetorthio/johm
Edit pom.xml
change Jedis version from 1.5.1 to 2.4.2 or latest
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.4.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
Compile project and build jar , use this new jar in your project

Problem making sense of doctrine 2

I'm having a hard time figuring out how to properly use Doctrine 2 with zend framework. I'm reading the docs and basing what I've done so far on that and the zendcasts. The problems actually start when I try to do relational stuff with my db, since I'm not so sure how to use doctrine collections. In my test case, I have an User entity:
class User
{
/**
* #var integer
* #Column (name="id", type="integer", nullable=false)
* #Id
* #GenerateValue(strategy="IDENTIY")
*
*/
private $id;
/**
* #Column(type="string",length=60,nullable=true)
* #var string
*/
private $email;
/**
*
* #param \Doctring\Common\Collections\Collection $property
* #OneToMany(targetEntity="Countries",mappedBy="user", cascade={"persist", "remove"})
*/
private $countries;
public function __get($property)
{
return $this->$property;
}
public function __set($property, $value)
{
$this->$property = $value;
}
}
Which is related to the countries entity:
class Countries {
/**
* #var integer
* #Column (name="id", type="integer", nullable=false)
* #Id
* #GenerateValue(strategy="IDENTIY")
*
*/
private $id;
/**
*
* #var string
* #Column(type="string")
*/
private $countryName;
/**
*
* #var User
* #ManyToOne(targetEntity="User")
* #JoinColumns({
* #JoinColumn(name="user_id", referencedColumnName="id")
* })
*/
private $user;
public function __get($property)
{
return $this->$property;
}
public function __set($property, $value)
{
$this->$property = $value;
}
}
Now I can assign the countries from the controller with something like this:
$p1 = new \Federico\Entity\Countries();
$p1->countryName = 'Argentina';
$p2 = new \Federico\Entity\Countries();
$p2->countryName = 'España';
$u = new \Federico\Entity\User();
$u->firstname = 'John';
$u->lastname = 'Doe';
$u->id = 1;
which would show me this object:
object(Federico\Entity\User)[109]
private 'id' => int 1
private 'email' => null
private 'countries' =>
array
0 =>
object(Federico\Entity\Countries)[107]
private 'id' => null
private 'countryName' => string 'Argentina' (length=9)
private 'user' => null
1 =>
object(Federico\Entity\Countries)[108]
private 'id' => null
private 'countryName' => string 'España' (length=7)
private 'user' => null
public 'firstname' => string 'John' (length=4)
public 'lastname' => string 'Doe' (length=3)
If you pay attention to this, you'll see that the user property is set to null in the country objects. I don't understand if this is supposed to happen like this or not. Also, since users will be allowed to choose the countries from a checkbox list, and they'll be able to choose more than one country,...shouldn't the countries somehow be stored in the Db?
I don't see where you assign country to a user in your code. Anyway, you need to do two things:
Initialize $countries variable as a new Doctrine\Common\Collections\ArrayCollection in User constructor.
Manually connect country with user:
public function addCountry(Country $c)
{
if (!$this->countries->contains($c)) {
$this->countries->add($c);
$c->user = $user;
}
}
In doctrine 2 the use of the magic getters and setters is discouraged. As you can see they can cause problems for managing associations. Below is an example of how to manage the association that you have in your User entity.
namespace Whatever/Your/Namespace/Is;
use \Doctrine\Common\ArrayCollection;
class User
{
/**
* #Column (type="integer")
* #Id
* #var integer
*/
private $id;
/**
* #OneToMany(targetEntity="Country", mappedBy="user", cascade={"persist", "remove"})
* #var ArrayCollection
private $countries;
public function __construct()
{
$this->countries = new ArrayCollection();
}
public function getCountries()
{
return $this->countries;
}
public function setCountry(Country $country)
{
$this->country[] = $country;
$country->setUser($this);
return $this;
}
public function removeCountry(Country $country)
{
return $this->country->removeElement($country);
}
}
and for Country
class Country
{
/**
* #Id
* #Column(type="integer")
* #var integer
*/
private $id;
/**
* #Column(length=100)
* #var string
*/
private $country_name;
/**
* #ManyToOne(targetEntity="User", inversedBy="countries")
* #var User
*/
private $user;
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
public function getUser()
{
return $this->user;
}
// ...
}

Resources