1
votes

I am new to Symfony and trying to setup entities and relationships. Even though I seemed to have correctly annotated the primary keys.

When running

php bin/console doctrine:schema:validate

I got an error as follows:

The referenced column name 'brandId' has to be a primary key column on the target entity class 'AppBundle\Entity\Brands'.

The entities look like ( only relevant portions):

Thanks

Brands

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 * @ORM\Table(name="brands")
 */
class Brands
{
/**
 * @ORM\Id
 * @ORM\Column(type="smallint",length=3,unique=true,options={"unsigned":true})
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $brandId;

/**
 * one brands has many models
 * @ORM\OneToMany(targetEntity="Models", mappedBy="brandId")
 * @ORM\JoinColumn(name="brandId", referencedColumnName="brandId")
 */
private $models;

public function __construct()
{
    $this->models = new ArrayCollection();
}

Models:

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;


/**
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ModelsRepository")
 * @ORM\Table(name="models")
 */
class Models
{
/**
 * @ORM\Column(type="smallint",length=4,unique=true,options={"unsigned":true})
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $modelId;

/**
 * @ORM\Column(type="string", length=25)
 */
private $model;

/**
 * Many models for one brand
 * @ORM\ManyToOne(targetEntity="Brands",inversedBy="models")
 * @ORM\JoinColumn(name="brandId", referencedColumnName="brandId")
 */
private $brandId;
4
Just follow the example here: docs.doctrine-project.org/projects/doctrine-orm/en/latest/… After fixing your relations, change Model::brandId to Model::brandCerad
@Cerad. thanks but why would I include ::brand which is the field for the brand name ( like Ford, BMW etc )? It's certainly not driving ( pun intended ) any of the relations.BernardA
Because brand is the brand object. brandId is merely the unique identifier. For Doctrine 2 try to think in terms of objects and not database column names.Cerad

4 Answers

1
votes

A valid and clean entity mapping should be something like:

Brand.php

/**
 * @ORM\Entity
 * @ORM\Table(name="brands")
 */
class Brand
{
    /**
     * @var integer
     *
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * one brands has many models
     *
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Model", mappedBy="brand")
     */
    private $models;

    public function __construct()
    {
        $this->models = new ArrayCollection();
    }
}

Model.php

class Model
{
    /**
     * @var integer
     *
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=25)
     */
    private $name;

    /**
     * Many models for one brand
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Brand",inversedBy="models")
     */
    private $brand;
}

for clean and easy usage:

$brand->getId(); //get id of brand
$brand->getModels(); //get array of Model object, ArrayCollection
$model->getBrand()->getId(); // Get id of related brand of some model 
$model->getBrand()->getName(); //get the name of other propery of related brand
1
votes

In your Brands model, this annotation should not be present.

* @ORM\JoinColumn(name="brandId", referencedColumnName="brandId")

JoinColumn only applies to ManyToOne and OneToOne fields. This is because in a one to many relationship, there will not be a join column in the table that contains the data for the owning ("one") side of the relationship.

JoinColumn is for defining a column on the "many" side that identifies which record on the "one" side owns it, so including it in the Models model is okay.

0
votes

Wrong answer removed...

Bonus tips: Entity classes or better their instances should represent single datasets of a table. So their names should be singular ;) By chosing not prefixed names for your primary key columns, you can spare some code, because Doctrine can work its magic then. Also you should not prefix one column with the table name, if you do not prefix all of them.

0
votes

This all ended up being an issue with Doctrine's naming strategy, which I did not know about. See this article for details

That strategy was set as default, while I was trying to adapt mysql's underscore naming strategy to PHP's camel case naming strategy. This was causing Symfony not to find the primary and thus the error.

Once I learned about that and with the valuable input I've received with this post, I have refactored the entities as below:

Thank you all.

Brands

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * @ORM\Entity
 * @ORM\Table(name="brands")
 */
class Brands
{
/**
 * @var integer
 * 
 * @ORM\Id
 * @ORM\Column(type="smallint",length=3,unique=true,options={"unsigned":true})
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * one brands has many models
 * 
 * @ORM\OneToMany(targetEntity="Models", mappedBy="brandId")
 */
private $models;

public function __construct()
{
    $this->models = new ArrayCollection();
}

/**
 * @ORM\Column(type="string", length=25)
 */
private $brand;
.......
}

Models

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;


/**
 * @ORM\Entity(repositoryClass="AppBundle\Repository\ModelsRepository")
 * @ORM\Table(name="models")
 */
class Models
{
/**
 * @var integer
 *
 * @ORM\Id
 * @ORM\Column(type="smallint",length=4,unique=true,options={"unsigned":true})
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * @ORM\Column(type="string", length=25)
 */
private $model;

/**
 * Many models for one brand
 * @ORM\ManyToOne(targetEntity="Brands",inversedBy="models")
 * @ORM\JoinColumn(name="brand_id", referencedColumnName="id")
 */
private $brandId;

/**
 * Many models for one segment
 * @ORM\ManyToOne(targetEntity="Segments", inversedBy="models")
 * @ORM\JoinColumn(name="segment_id", referencedColumnName="id")
 */
private $segmentId;
.....
 }