Reputation: 851
I'm new to Symfony and these questions were brought about in the recent course of learning.
Take a store as an example, I'll create two entities, Product and Category, which have a bidirectional Many-to-One relationship.
class Product
{
private $id;
private $name;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Category", inversedBy="products")
* @ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false)
*/
private $category;
}
class Category
{
private $id;
private $name;
/**
* @ORM\OneToMany(targetEntity="App\Entity\Product", mappedBy="category")
*/
private $products;
}
So my 1st question is:
If I want to get all the products in a particular category, should the URL be
/categories?categoryId=1&limit=20&orderBy=name (I know it's a bit silly, but should a Category record contains all the Product information?)
or
/products?categoryId=1&limit=20&orderBy=name
For the latter one, here comes the 2nd question:
I injected the ProductRepository to the ProductController
class ProductController extends Controller
{
private $productRepository;
public function __construct(ProductRepository $productRepository)
{
$this->productRepository = $productRepository;
}
...
}
In order to get all the product in a category, I wrote a method like this:
public function findByCategory(Category $category): array
{
return $this->createQueryBuilder('p')
->andWhere('p.category = :category')
->setParameter('category', $category)
->orderBy('p.name', 'ASC')
->setMaxResults(20)
->getQuery()
->getResult()
;
}
So, in the ProductController, how should I get the Category object from the query string 'categoryId' in the URL? Should I inject CategoryRepository as well or should I simply inject an entity manager object?
Upvotes: 0
Views: 753
Reputation: 946
To me, the problem here is a very clear confusion between what the ORM does in Symfony and Database Design, Modelling and Querying.
In the database (using PhpMyAdmin), you'll notice that on the product table there is a column called category (or category_id). Keep it simple, to get products belonging to a category, all you need is the category_id. Take that knowledge to Symfony, you DO NOT need the Category Object, please just use the category ID that you got from the request. Also, just use the EntityManager in the controller, don't complicate stuff, especially since it seems you're just starting out.
use Symfony\Component\HttpFoundation\Request;
class ProductController extends Controller
{
public function get_product_from_categoryAction(Request $request)
{
$category_id = (int) $request->get('category');
$limit = (int) $request->get('limit');
$orderBy = strip_tags($request->get('orderBy'));
$em = $this->getDoctrine()->getManager();
$products = $em
->getRepository('AppBundle:Products')
->queryProductsByCategoryId($category_id, $limit, $orderBy);
}
...
}
And the repo
public function queryProductsByCategoryId(int $category_id, int $limit = 10, string $orderBy = 'name')
{
return $this->createQueryBuilder('p')
->andWhere('p.category = :category')
->setParameter('category', $category_id)
->orderBy('p.name', 'ASC')
->setMaxResults($limit)
->getQuery()
->getResult()
;
}
Keep things simple, then when you got to be more advanced, try the more fancy stuff if you so please.
Upvotes: 0
Reputation: 1324
Marco Pivetta aka Ocramius (one of the main developers of Doctrine) said:
Avoid bi-directional associations
Bi-directional associations are overhead
Code only what you need for your domain logic to work
Hack complex DQL queries instead of making them simpler with bidirectionality
So maybe you don't need bi-directional association here.
For your first question, the second solution is better in my opinion:
/products?categoryId=1&limit=20&orderBy=name
For your second question yes, you should inject the CategoryRepository if you want to access Category object, avoid accessing the whole entityManager in your controller even if it is possible.
You should inject services in your controllers. Your services should expose public methods to perform custom CRUD access to the DB through data mappers. Note that a repository is not a data mapper but it
mediates between the domain and data mapping layers, acting like an in-memory domain object collection.
P of EAA Catalog - Martin Fowler
Repositories are services in fact so that's fine to inject them into the controller.
Some people defend the position that repositories should not contain CREATE, UPDATE or DELETE but only READ. They say that these operations make collections (which are accessible through repositories) inconsistent.
This post can help too: How should a model be structured in MVC?
Upvotes: 1