I've been trying to solve this for a few days with no luck.
First a few clarifications:
Basically I want to create relation between "Users" and "Stores", My plan in to identify favorites shops to a particular user and Also from the Store perspective, who shops in one particular store as their favorite shop. My guess was to use a ManyToMany Relation, But after looking for examples about this, many sites describe that if you require a ManyToMany relation with additional fields (in my case I have 3 additional fields) you have to use OneToMany/ManyToOne instead
I'm using SQLITE for my DB just for development and I'm writing REST API in PHP using Slim Framework
Everything works fine when getting a particular entity using something like this.
/**
* Fetching user by email
* @param String $email User email
*/
public function getUserByEmail($email) {
$user = $this->getEntityManager()->getRepository('User')
->findOneBy(array('email' => $email));
if ($user === null) {
return null;
} else {
return $user;
}
}
or
$ProductStore = $this->getEntityManager()->getRepository('ProductStore')
->findOneBy(array('product' => $product,
'store' => $store));
or
$Store = $this->getEntityManager()->getRepository('Store')
->findOneBy(array('state' => $State->getId(),
'city' => $City->getId(),
'streetname' => $streetname,
'streetnumber' => $streetnumber));
//Store has many ManyToOne relations to other Entities
//like State or City, but all of them works as expected
Once I get any entity i can read their attributes. Except when i try to obtain the:
echo $User->getShopsAt(); i get an error saying "Object of class Doctrine\ORM\PersistentCollection could not be converted to string"
if I try
echo [$User->getShopsAt()]; i get "Array" as reponse
if I try
echo count([$User->getShopsAt()]); i get 1
If i understand the call to getShopsAt() should return all instance of "UserStore" entity that match the User id, But I'm not able to access any of its properties. Everything I've tried fails, Also i want to point out that I used the User Id "1" that has 2 entries on the "UserStore" Table. So why is count returning 1.
I've created the 3 Schema using YML format, since i find it to be more clear than PHP.
With these Schema i have created my entities using the command "vendor/bin/doctrine orm:generate-entities ./src" (See resulting entities at the bottom)
Then I create the database schema using vendor/bin/doctrine orm:schema-tool:update --force (See database SQL, and data at the bottom)
After this I have created some data on the Database using the Advaced REST Client extension on Chrome Browser. BUT I've created all of them independently. This mean, First create the Users without setting anything on to shopsAt attribute, then created some Stores without setting anything to the shoppers attribute and then created some UserStores
IMPORTANT QUESTIONS: During the creation of UserStore i have to recover the actual User and Store to set their inversed side of the relation?
or
No just adding the correct keys to the UserStore and Doctrine will do the match during a read of the User/Store, since the creation of UserStore is enough because because it's the Owning side of the relation?
or
I have to manually modify the entity auto generated by the orm:generate-entities command and in the methods setStore and setUser of the UserStore to update the inversed side? as mentioned Here
My database looks like this:
USER:
CREATE TABLE users (id INTEGER NOT NULL, name VARCHAR(255) NOT NULL, email VARCHAR(50) NOT NULL, password_hash VARCHAR(255) NOT NULL, api_key VARCHAR(32) NOT NULL, status INTEGER NOT NULL, createdAt DATETIME NOT NULL, updatedAt DATETIME NOT NULL, PRIMARY KEY(id))
ROW 1 = "1","user1","user1@mail.com","xxxxxxxxxxxxxxxx","yyyyyyyyyyyyyy","1","2015-06-27 17:16:01","2015-06-27 17:16:01"
ROW 2 = "2","user2","user2@mail.com","xxxxxxxxxxxxxxxx","yyyyyyyyyyyyyy","1","2015-06-27 17:38:36","2015-06-27 17:38:36"
STORE:
CREATE TABLE stores (id INTEGER NOT NULL, brand_id INTEGER DEFAULT NULL, neighborhood_id INTEGER DEFAULT NULL, county_id INTEGER DEFAULT NULL, city_id INTEGER DEFAULT NULL, state_id INTEGER DEFAULT NULL, streetname VARCHAR(255) NOT NULL COLLATE BINARY, streetnumber VARCHAR(255) NOT NULL COLLATE BINARY, cp VARCHAR(255) NOT NULL COLLATE BINARY, lat VARCHAR(255) NOT NULL COLLATE BINARY, lng VARCHAR(255) NOT NULL COLLATE BINARY, favorite INTEGER NOT NULL, status INTEGER NOT NULL, createdAt DATETIME NOT NULL, updatedAt DATETIME NOT NULL, PRIMARY KEY(id), CONSTRAINT FK_D5907CCC44F5D008 FOREIGN KEY (brand_id) REFERENCES brands (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_D5907CCC803BB24B FOREIGN KEY (neighborhood_id) REFERENCES neighborhoods (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_D5907CCC85E73F45 FOREIGN KEY (county_id) REFERENCES counties (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_D5907CCC8BAC62AF FOREIGN KEY (city_id) REFERENCES cities (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_D5907CCC5D83CC1 FOREIGN KEY (state_id) REFERENCES states (id) NOT DEFERRABLE INITIALLY IMMEDIATE)
ROW 1 = "1","1","1","1","1","1","streetname 1","11111","1234","0.0000000","1.1111111","1","1","2015-06-06 02:32:19","2015-06-06 02:32:19"
ROW 2 = "2","4","2","2","2","2","streetname 2","2222","1234","0.000000","1.000000","0","1","2015-06-27 17:50:24","2015-06-27 17:50:24"
USERSTORE:
CREATE TABLE UsersStores (user_id INTEGER NOT NULL, store_id INTEGER NOT NULL, status INTEGER NOT NULL, createdAt DATETIME NOT NULL, updatedAt DATETIME NOT NULL, PRIMARY KEY(user_id, store_id), CONSTRAINT FK_39EFBEF8A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_39EFBEF8B092A811 FOREIGN KEY (store_id) REFERENCES stores (id) NOT DEFERRABLE INITIALLY IMMEDIATE)
VALUES:
ROW 1 = "2","1","1","2015-06-28 17:51:40","2015-06-28 17:51:40"
ROW 2 = "1","2","1","2015-06-28 17:51:51","2015-06-28 17:51:51"
ROW 3 = "1","1","1","2015-06-28 20:51:53","2015-06-28 20:51:53"
User:
type: entity
table: users
id:
id:
type: integer
generator:
strategy: auto
fields:
name:
type: string(255)
email:
type: string(50)
password_hash:
type: string(255)
api_key:
type: string(32)
status:
type: integer(1)
createdAt:
type: datetime
updatedAt:
type: datetime
oneToMany:
shopsAt:
targetEntity: UserStore
mappedBy: product
///////////////////////////////////
Store:
type: entity
table: stores
id:
id:
type: integer
generator:
strategy: AUTO
fields:
streetname:
type: string
streetnumber:
type: string(255)
cp:
type: string(255)
lat:
type: string(255)
lng:
type: string(255)
favorite:
type: integer(1)
status:
type: integer(1)
createdAt:
type: datetime
updatedAt:
type: datetime
oneToMany:
productsAvailable:
targetEntity: ProductStore
mappedBy: store
shoppers:
targetEntity: UserStore
mappedBy: store
manyToOne:
brand:
targetEntity: Brand
joinColumn:
name: brand_id
referencedColumnName: id
neighborhood:
targetEntity: Neighborhood
joinColumn:
name: neighborhood_id
referencedColumnName: id
county:
targetEntity: County
joinColumn:
name: county_id
referencedColumnName: id
city:
targetEntity: City
joinColumn:
name: city_id
referencedColumnName: id
state:
targetEntity: State
joinColumn:
name: state_id
referencedColumnName: id
//////////////////////////////////////
UserStore:
type: entity
table: UsersStores
id:
user:
associationKey: true
store:
associationKey: true
fields:
status:
type: integer(1)
createdAt:
type: datetime
updatedAt:
type: datetime
manyToOne:
user:
targetEntity: User
inversedBy: shopsAt
joinColumn:
name: user_id
referencedColumnName: id
store:
targetEntity: Store
inversedBy: shoppers
joinColumn:
name: store_id
referencedColumnName: id
//////////////////////////////////////
use Doctrine\ORM\Mapping as ORM;
/**
* User
*/
class User
{
/**
* @var integer
*/
private $id;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $email;
/**
* @var string
*/
private $password_hash;
/**
* @var string
*/
private $api_key;
/**
* @var integer
*/
private $status;
/**
* @var \DateTime
*/
private $createdAt;
/**
* @var \DateTime
*/
private $updatedAt;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set email
*
* @param string $email
* @return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set password_hash
*
* @param string $passwordHash
* @return User
*/
public function setPasswordHash($passwordHash)
{
$this->password_hash = $passwordHash;
return $this;
}
/**
* Get password_hash
*
* @return string
*/
public function getPasswordHash()
{
return $this->password_hash;
}
/**
* Set api_key
*
* @param string $apiKey
* @return User
*/
public function setApiKey($apiKey)
{
$this->api_key = $apiKey;
return $this;
}
/**
* Get api_key
*
* @return string
*/
public function getApiKey()
{
return $this->api_key;
}
/**
* Set status
*
* @param integer $status
* @return User
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* @return integer
*/
public function getStatus()
{
return $this->status;
}
/**
* Set createdAt
*
* @param \DateTime $createdAt
* @return User
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set updatedAt
*
* @param \DateTime $updatedAt
* @return User
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* @var \Doctrine\Common\Collections\Collection
*/
private $shopsAt;
/**
* Constructor
*/
public function __construct()
{
$this->shopsAt = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add shopsAt
*
* @param \UserStore $shopsAt
* @return User
*/
public function addShopsAt(\UserStore $shopsAt)
{
$this->shopsAt[] = $shopsAt;
return $this;
}
/**
* Remove shopsAt
*
* @param \UserStore $shopsAt
*/
public function removeShopsAt(\UserStore $shopsAt)
{
$this->shopsAt->removeElement($shopsAt);
}
/**
* Get shopsAt
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getShopsAt()
{
return $this->shopsAt;
}
}
///////////////////////////////////////
use Doctrine\ORM\Mapping as ORM;
/**
* Store
*/
class Store
{
/**
* @var integer
*/
private $id;
/**
* @var string
*/
private $streetname;
/**
* @var string
*/
private $streetnumber;
/**
* @var string
*/
private $cp;
/**
* @var string
*/
private $lat;
/**
* @var string
*/
private $lng;
/**
* @var integer
*/
private $favorite;
/**
* @var integer
*/
private $status;
/**
* @var \DateTime
*/
private $createdAt;
/**
* @var \DateTime
*/
private $updatedAt;
/**
* @var \Doctrine\Common\Collections\Collection
*/
private $productsAvailable;
/**
* @var \Brand
*/
private $brand;
/**
* @var \Neighborhood
*/
private $neighborhood;
/**
* @var \County
*/
private $county;
/**
* @var \City
*/
private $city;
/**
* @var \State
*/
private $state;
/**
* Constructor
*/
public function __construct()
{
$this->productsAvailable = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set streetname
*
* @param string $streetname
* @return Store
*/
public function setStreetname($streetname)
{
$this->streetname = $streetname;
return $this;
}
/**
* Get streetname
*
* @return string
*/
public function getStreetname()
{
return $this->streetname;
}
/**
* Set streetnumber
*
* @param string $streetnumber
* @return Store
*/
public function setStreetnumber($streetnumber)
{
$this->streetnumber = $streetnumber;
return $this;
}
/**
* Get streetnumber
*
* @return string
*/
public function getStreetnumber()
{
return $this->streetnumber;
}
/**
* Set cp
*
* @param string $cp
* @return Store
*/
public function setCp($cp)
{
$this->cp = $cp;
return $this;
}
/**
* Get cp
*
* @return string
*/
public function getCp()
{
return $this->cp;
}
/**
* Set lat
*
* @param string $lat
* @return Store
*/
public function setLat($lat)
{
$this->lat = $lat;
return $this;
}
/**
* Get lat
*
* @return string
*/
public function getLat()
{
return $this->lat;
}
/**
* Set lng
*
* @param string $lng
* @return Store
*/
public function setLng($lng)
{
$this->lng = $lng;
return $this;
}
/**
* Get lng
*
* @return string
*/
public function getLng()
{
return $this->lng;
}
/**
* Set favorite
*
* @param integer $favorite
* @return Store
*/
public function setFavorite($favorite)
{
$this->favorite = $favorite;
return $this;
}
/**
* Get favorite
*
* @return integer
*/
public function getFavorite()
{
return $this->favorite;
}
/**
* Set status
*
* @param integer $status
* @return Store
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* @return integer
*/
public function getStatus()
{
return $this->status;
}
/**
* Set createdAt
*
* @param \DateTime $createdAt
* @return Store
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set updatedAt
*
* @param \DateTime $updatedAt
* @return Store
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Add productsAvailable
*
* @param \ProductStore $productsAvailable
* @return Store
*/
public function addProductsAvailable(\ProductStore $productsAvailable)
{
$this->productsAvailable[] = $productsAvailable;
return $this;
}
/**
* Remove productsAvailable
*
* @param \ProductStore $productsAvailable
*/
public function removeProductsAvailable(\ProductStore $productsAvailable)
{
$this->productsAvailable->removeElement($productsAvailable);
}
/**
* Get productsAvailable
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getProductsAvailable()
{
return $this->productsAvailable;
}
/**
* Set brand
*
* @param \Brand $brand
* @return Store
*/
public function setBrand(\Brand $brand = null)
{
$this->brand = $brand;
return $this;
}
/**
* Get brand
*
* @return \Brand
*/
public function getBrand()
{
return $this->brand;
}
/**
* Set neighborhood
*
* @param \Neighborhood $neighborhood
* @return Store
*/
public function setNeighborhood(\Neighborhood $neighborhood = null)
{
$this->neighborhood = $neighborhood;
return $this;
}
/**
* Get neighborhood
*
* @return \Neighborhood
*/
public function getNeighborhood()
{
return $this->neighborhood;
}
/**
* Set county
*
* @param \County $county
* @return Store
*/
public function setCounty(\County $county = null)
{
$this->county = $county;
return $this;
}
/**
* Get county
*
* @return \County
*/
public function getCounty()
{
return $this->county;
}
/**
* Set city
*
* @param \City $city
* @return Store
*/
public function setCity(\City $city = null)
{
$this->city = $city;
return $this;
}
/**
* Get city
*
* @return \City
*/
public function getCity()
{
return $this->city;
}
/**
* Set state
*
* @param \State $state
* @return Store
*/
public function setState(\State $state = null)
{
$this->state = $state;
return $this;
}
/**
* Get state
*
* @return \State
*/
public function getState()
{
return $this->state;
}
/**
* @var \Doctrine\Common\Collections\Collection
*/
private $shoppers;
/**
* Add shoppers
*
* @param \UserStore $shoppers
* @return Store
*/
public function addShopper(\UserStore $shoppers)
{
$this->shoppers[] = $shoppers;
return $this;
}
/**
* Remove shoppers
*
* @param \UserStore $shoppers
*/
public function removeShopper(\UserStore $shoppers)
{
$this->shoppers->removeElement($shoppers);
}
/**
* Get shoppers
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getShoppers()
{
return $this->shoppers;
}
}
//////////////////////////////////////////////////
use Doctrine\ORM\Mapping as ORM;
/**
* UserStore
*/
class UserStore
{
/**
* @var integer
*/
private $status;
/**
* @var \DateTime
*/
private $createdAt;
/**
* @var \DateTime
*/
private $updatedAt;
/**
* @var \User
*/
private $user;
/**
* @var \Store
*/
private $store;
/**
* Set status
*
* @param integer $status
* @return UserStore
*/
public function setStatus($status)
{
$this->status = $status;
return $this;
}
/**
* Get status
*
* @return integer
*/
public function getStatus()
{
return $this->status;
}
/**
* Set createdAt
*
* @param \DateTime $createdAt
* @return UserStore
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set updatedAt
*
* @param \DateTime $updatedAt
* @return UserStore
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
/**
* Set user
*
* @param \User $user
* @return UserStore
*/
public function setUser(\User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* @return \User
*/
public function getUser()
{
return $this->user;
}
/**
* Set store
*
* @param \Store $store
* @return UserStore
*/
public function setStore(\Store $store)
{
$this->store = $store;
return $this;
}
/**
* Get store
*
* @return \Store
*/
public function getStore()
{
return $this->store;
}
}