Symfony 3 Blog Demo Remix (Part 3): Creating a Category entity
Creating a Category entity
For this part of this tutorial, we are going to be extending the application by creating a Category
entity which will get tied to each Post
entity (via an M:1 relationship). You may have to read the first and second parts of these tutorial series to complete your application.
But for this third part, at first glance, this task doesn't seem like it's going to be too tough to create (which really isn't). However, before making too many assumptions, we should create a quick list of what our entity needs to do and where it needs to go.
Designing the Category entity
My site is called Code As A Last Resort... That being said, let's save the coding part until the very end, when we have no further assumptions to make or possible caveats to overcome. Here is a bare-bones list of what this "category" extension is going to need:
- Create a
Category
class with the namespaceAppBundle\Entity
to define which properties our new entity should have. - The
Category
class should have some basic fields as well as getters and setters to access each field:
- Category Name
- Category Status
- A Many-to-One relationship to the
Post
entity - An ID field to properly define the entity and establish a primary key
- A date that category was created
- A slug for using in the URL for SEO purposes
- An
isDeleted
flag to indicate if a category is considered deleted in the system
- Create a form for creating new categories in the backend
- Create a controller that will handle the category entities displayed on the front & backend
- Modify the
Pos
t entity to include a "category" field which will have a one-to-many relationship with the post - Auto update our database to reflect our new entity
- To keep a clear separation of concerns, create a new repository for the category entity to define any custom queries or operations it may have
Damnit! That's actually quite a bit of work when you break it down. No worries though its not as hard as it may seem. Let's get started.
A category obviously needs a name (like "stories", "news", "updates", etc). In addition, each category should include a status field that will be used to track if it's been deleted and/or if the category is active and should be displayed on the page. This is more desirable than just deleting it completely from the database because you still have a record of what it was. Finally, the category
entity should have a relationship with the Post
entity to properly be able to track which posts belong to which category. We will configure this relationship directly in the Category
and Post
entities (under AppBundle\Entity
namespace) via annotations.
The Category entity
src/AppBundle/Entity/Category.php</
/*
* This file is part of Symfony3 Blog Demo REMIX package.
*
* (c) Jesse Griffin <codeasalastresort@gmail.com>
*
* The Category Entity
*/
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* @ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
* @ORM\Table(name="category")
*
* Defines the properties of the Category entity to represent the blog categories.
*
* @author Jesse Griffin <hemham914@gmail.com>
*/
class Category
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string")
* @Assert\NotBlank()
*/
private $name;
/**
* @ORM\Column(type="string",nullable=true)
*/
private $slug;
/**
* @ORM\Column(type="datetime")
* @Assert\DateTime()
*/
private $createdAt;
/**
* @ORM\Column(type="boolean")
*/
private $isDeleted = false;
public function __construct()
{
$this->createdAt = new \DateTime();
$this->isDeleted = false;
}
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @return mixed
*/
public function getName()
{
return $this->name;
}
/**
* @param mixed $name
* @return Category
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @return mixed
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* @param mixed $createdAt
* @return Category
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
public function getSlug()
{
return $this->slug;
}
public function setSlug($slug)
{
$this->slug = $slug;
return $this;
}
public function isDeleted()
{
return $this->isDeleted;
}
public function delete()
{
$this->isDeleted = true;
return $this;
}
}
This entity is pretty self-explanatory. It has some basic fields that, together, comprise the properties that will be used to create categories for our posts. The only notable thing here is the doctrine annotations, which we used to describe each field from a database perspective.
Creating a Repository for the Category entity
If you take a look inside the src/AppBundle/Repository
directory, you will see that there are actually two repositories that come stock with the Symfony 3 Blog Demo. Examine the PostRepository.php
and the UserRepository.php
files briefly to get a better idea of what we need to build for our Category entity. If you are completely clueless as to what a repository is, this tutorial will help.
Basically what a repository does is act as a container that holds any custom queries specific to a particular entity. There are multiple reasons as to why you would want to build your database objects in this manner:
- It keeps lengthy and complex queries isolated from the controller code (separation of concerns).
- It makes it possible to unit test the queries in isolation and makes automation easier when building a testing implementation for your application.
- Code reuse is possible when using repositories to hide all the complex queries an entity might need.
Thanks to Doctrine (and Symfony of course), a repository is as simple to build as writing an extra line of code in the entity annotation definition and running one command. For our Category entity, you will notice that the annotation defining the class as an entity contains a repository annotationthat looks like this:
@ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
This is what tells the doctrine that there is a repository class that is associated with that particular entity class (although it doesn't exist quite yet). What's really neat about this mechanism is that you can generate the repository from scratch with just a single Doctrine command (the repository class name must be specified in the @ORM\Entity
annotation as it is above):
php bin/console doctrine:generate:entities AppBundle
When we run this command, the entity's repository class is created in the AppBundle\Repository
namespace and is named...you guessed it, CategoryRepository.php
. Open up this file and you should see something similar to the other repository classes under the AppBundle\Repository
namespace.
One thing to note about this command is that you can also run it to automatically generate any missing getters and setters from your entity class. Pretty handy indeed!
Inserting custom query logic into the Category repository
At this point, any custom query logic that the particular entity needs can be added in the form of a new class method. For instance, for our Category
entity, we are going to want, at some point, to pull in all categories in the database that are not marked as "deleted" (i.e. have an active status) and sorted in an ascending order by their respective "name" field. To accomplish this, we would create a new method named findAllOrderedByNameAsc()
and it might look something like this:
public function findAllOrderedByNameAsc()
{
return $this->getEntityManager()
->createQuery(
'SELECT c FROM AppBundle::Category c ORDER BY c.name ASC'
)
->getResult();
}
Something to note here is that the Doctrine entity manager can be accessed anywhere within the repository class via a call to `$this->getEntityManager()```.
From there, you can query the database directly, retrieve a specific repository from the entity manager, create transactions and a bunch of other useful things as well. For more info on the entity manager and its capabilities, check out the documentation here.
Now that we have our Category
entity thought out and coded, we need to tell Doctrine of its existence so to have it build the related database tables that we need in order for this to function.
We will get to that on the next section of the tutorial. Keep checking back!
Complete series:
This tutorial is originally posted by the author on his blog. This version has been edited for clarity.
Every router has private IP address, try to login with Default Username/Password: https://192-168-1-254.me/
Everyone has ideas and their own life experience and so the experiences you have had can be used to https://helixjump2.com/helix-jump-2
Big thanks https://texttwist.online to the admin for this idea