简体   繁体   中英

Doctrine/Symfony3 - Database columns with specific values

I'm trying to build a doctrine database for a Symfony application, and I'm not sure of the correct way of assembling the schema.

From here on in I'll use the Symfony3 tutorial Product and Category entities to illustrate my confusion:

// src/AppBundle/Entity/Category.php
namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="category")
 */
class Category
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

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

}

How do I define type so that it is one of a select few values (say: ["car", "bike", "bus"] )?

My first initial thought was to have validation inside Symfony itself, such that before pushing to the database a check occurs.

However, this feels slightly hacky to me, and I'm wondering whether the correct procedure would be to enforce this constraint inside Doctrine itself.

Can someone shed some light on the best way to do this?

My problem further complicates itself when I have a one-to-many relationship between Category and Product , and Product itself has a type variable. My issue is that the valid values for Product:type differ depending on Category:type , for example:

  • Category:type = "car" -> Product:type = ["BMW" OR "Volvo"]
  • Category:type = "bike" -> Product:type = ["BMX" OR "Mountain Bike"]

Should this all be done using form/controller validation, or am I correct in assuming that it should be handled at a "lower" level?

I think you should create a separate product type table that stores the category relationship and is itself stored in the product instead of putting the category in the product.

So you would have

 Category 

 Product Type

 Product

Product type is many to one categories, a category may contain many product types, a Product is many to one product types, a product type may contain many products.

  Category Type -> Product Type -> Product

Make sense, there is no need to store the category in the product because it is inferred by the product type and its relationship.

Just to clarify,

  Category
  --------
     id                //such as 1
     title             //such as Car
     description

  Product Type
  --------
     id                //such as 1
     category_id       //  id from Category 
     title             //such as BMW
     description


  Product
  --------
     id                //such as 6
     product_type_id   // id from Product Type
     title             //2016  New BMW
     description

Then with a join you could do

   Select
       c.title As category, pt.title As type, p.title
   From products As p
   Join product_type As pt
      ON p.product_type_id = pt.id
   Join category As c
      ON p.category_id = c.id
   Where
     p.id = 6

Then it should say

   ['category'=>'Car', 'type' => 'BMW', 'title'=> '2016  New BMW']

Now if you want product types to be in more then one category you could /should do it this way

Category
--------
 id                //such as 1
 title             //such as Car
 description

Category Type   ///Bridge table,  (category_id & type_id are unique togather )
--------             
 id                 // you could leave this id out, but it simplifies some things (see below)
 category_id       //  id from Category 
 type_id           //  id from Type

Type
--------
 id                //such as 1
 title             //such as BMW
 description

Product
--------
 id                //such as 6
 category_type_id   // id from Category Type
 title             //2016  New BMW
 description

It makes things more complicated but that's what happens when you have complex relation ships like Many to Many. For the query you would add that

Select
    c.title As category, t.title As type, p.title
From
    products As p
Join
    type As t
    ON p.category_type_id = ct.id
Join
    category_type As pt
    ON t.id = ct.type_id 
Join
    category As c
    ON pt.category_id = c.id
Where
    p.id = 6

Of course you probably dont want to use title in them all, i was just not feeling particularly creative. In this last one if you can use the bridge table ID you can skip one of the tables if you dont need to reference it. So if you only wanted the category and product info,

Select
    c.title As category, p.title
From
    products As p
Join
    category_type As ct
    ON p.category_type_id = ct.id
Join
    category As c
    ON ct.category_id = c.id
Where
    c.id = 6

This pattern is essentially like a Y with the type and category at the top and the product on the bottom, and the nexus ( where they join ) the category_type table. Where if you don't use an id there ( in category type ) its liner, and you wouldn't be able to skip over one of the tables ( from c to p ). That would be ok because the two foreign keys ( category_id and type_id ) should be unique when combined ( compound key ). So its like this

Many to Many ( 1 ) Y shape ( not sure the technical term lol )

      Category-> 
                 CategoryType -> Product
      Type ->

Vs Many to Many ( 2 ) Liner

      Category-> CategoryType -> Type -> Product

So in the first one ( 1 ) you can see you can leave one side off like this

                 CategoryType -> Product
      Type ->

OR ( as shown in the above query )

      Category-> 
                 CategoryType -> Product

Vs in the second ( 2 )

       Category-> CategoryType ->         Product

You would have a hole, that you couldn't jump. From CategoryType to Product, because there is not a reference to the Category or the Category Type in the Product record and vise versa. The drawback though is you can only have one category_type per product. Where if you use the liner one you have one type per product. Visual the second one is like a funnel.

In any case then you have a many to many between Category and Type, so you could have Many categories with many (product) types. For example you may have product types of

       Clothes -> Adidas
       Footwear -> Adidas
       Clothes -> Levis

So the record ( Type ) of Adidas belongs to both Clothes and Footwear ( Categories ) but Clothes also has other types such as Levis. So it is Many to Many, this is a bit harder to deal with but it's probably closer to what you need, which is why I wanted to explain it as best I could.

I guess in summery there are many ways to do this, you just need to consider beforehand what the relationship is and how flexible you need it. The last one I will do is what I would call tags.

  Tag
  --------
     id                //such as 1
     title             //such as Car
     description

  Product Tags
  --------
     tag_id          //  id from Tag
     product_id      // id from Product

  Product
  --------
     id                //such as 6
     product_type_id   // id from Product Type
     title             //2016  New BMW
     description

Here we have a many to many between the product and the tag, so you can put as many tags on as many products as you want. But you lose the structure of Adidas always being Footware, or Clothes for example.

The "ultimate" would be to have a bridge table between all three parts.

   Category -> ( bridge ) -> Type -> ( bridge ) -> product.

That gets very complicated, but you could have 2 many to many relationships so products could belong to many types, which could belong to many categories. Personally I would go with the Tags, its almost as flexible and easier to deal with. It just takes more selection when creating the products.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM