I have extended entity via EntityExtendBundle like this:
...
use App\Bundle\MyBundle\Model\EntityModel;
...
/**
* #ORM\Entity(...)
* #ORM\Table(name="entity_table"...)
* #Config(...)
*/
class SomeEntity extends SomeEntityModel
{
.....
}
Now it needed separate SomeEntity for 2 types. I'm trying do it via Symfony extending:
* #ORM\Entity(...)
* #ORM\Table(name="entity_table"...)
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="entity_type", type="string", length=32 )
* #DiscriminatorMap({"type1" = "FirstEntity", "type2" = "SecondEntity"})
* #Config(...)
*/
class SomeAbstractEntity extends SomeEntityModel
{
.....
}
And two inheritors. 1-st entity:
/**
* #ORM\Entity
* #Config()
*/
class FirstEntity extends SomeAbstractEntity
{
}
...adn 2-nd:
/**
* #ORM\Entity
* #Config()
*/
class SecondEntity extends SomeAbstractEntity
{
}
But when I build project
...
docker-compose exec fpm bash -c 'bin/console oro:entity-config:update'
docker-compose exec fpm bash -c 'bin/console oro:entity-extend:update-config'
docker-compose exec fpm bash -c 'bin/console oro:entity-extend:update-schema'
...
... ERROR: Table "entity_table" has more than 1 class. This is not supported by ExtendExtension.
Is it possible to separate extended via EntityExtendBundle entity for 2 types?
Related
I am using Slim Framework with Doctrine. I have three Tables
id | username | password | name
--------------------------------
1 | Lorel | ******** | Lorel
id | permission | description
-------------------------------
2 | READ_ACCESS | Lorel Ipsum
id | user_id | permission_id
-----------------------------
X | 1 | 2
Is there anyway using doctrine through which I can find out, suppose if user '1' has permission '2'.
I'm assuming you're looking to do Authorization. I've got a setup which does that, in Zend Framework 3 with Doctrine 2. The relations are the same, just not sure how to translate it to Slim Framework. But here goes nothing ;-)
User Entity has a relation to Roles:
/**
* #var Collection|ArrayCollection|Role[]
* #ORM\ManyToMany(targetEntity="User\Entity\Role", inversedBy="users", fetch="LAZY")
* #ORM\JoinTable(
* name="user_user_roles",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*
*/
protected $roles;
Role Entity has Routes and the inverse side to User
/**
* #var Collection|ArrayCollection|Route[]
* #ORM\ManyToMany(targetEntity="User\Entity\Route", inversedBy="roles", fetch="EAGER")
* #ORM\JoinTable(
* name="user_role_routes",
* joinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="route_id", referencedColumnName="id")}
* )
*/
protected $routes;
/**
* #var Collection|ArrayCollection|User[]
* #ORM\ManyToMany(targetEntity="User\Entity\User", mappedBy="roles", fetch="LAZY")
*/
protected $users;
Route Entity just has the inverse to Role
/**
* #var Collection|ArrayCollection|Role[]
* #ORM\ManyToMany(targetEntity="User\Entity\Role", mappedBy="routes", fetch="LAZY")
*/
protected $roles;
Notice that it concerns 2 relationships:
User <-> Role
Role <-> Route
Make sure to initialize each Collection in the __construct, like so:
// Initialize only those within the Entity
public function __construct()
{
$this->users = new ArrayCollection();
}
Generate your getter method (setter not required!). Create Adder/Remover methods instead of a setter, like so (this is within Route Entity):
/**
* #param Collection|ArrayCollection|Role[] $roles
*
* #return Route
*/
public function addRoles(Collection $roles) : Route
{
foreach ($roles as $role) {
if ( ! $this->getRoles()->contains($role)) {
$this->getRoles()->add($role);
}
if ( ! $role->getRoutes()->contains($this)) {
$role->getRoutes()->add($this);
}
}
return $this;
}
/**
* #param Collection|ArrayCollection|Role[] $roles
*
* #return Route
*/
public function removeRoles(Collection $roles) : Route
{
foreach ($roles as $role) {
if ($this->getRoles()->contains($role)) {
$this->getRoles()->remove($role);
}
if ($role->getRoutes()->contains($this)) {
$role->getRoutes()->remove($this);
}
}
return $this;
}
So, there you go, that's the setup. I would advise you to include Gedmo Doctrine extensions and apply the #Gedmo\Tree(type="nested") to your Role Entity. Makes managing (nested/inherited) roles easy. See Managing Hierarchical Data in MySQL (and Gedmo Tree docs)
To next check if a User has access to a certain Route you need some form of AuthenticationService. Because I don't know Slim, make sure you fill this in with something from that framework. The logic is the same though. I use a service to be included/used on route access that checks if the User is known (Authenticated), and if not assigns a Guest Role, and then checks if the Route to be accessed is known to any of the assigned roles.
/**
* #param string $route
*
* #return bool
* #throws Exception
*/
public function isGranted(string $route) : bool
{
// Get assigned Role[] array or set Guest Role
if ($this->getAuthenticationService()->hasIdentity()) {
/** #var User $user */
$user = $this->getAuthenticationService()->getIdentity();
/** #var Collection|Role[] $roles */
$roles = $user->getRoles();
} else {
$roles = new ArrayCollection(
[
$this->getObjectManager()->getRepository(Role::class)->findOneBy(['name' => Role::NO_ACCOUNT_ROLE]),
]
);
}
foreach ($roles as $role) {
if ($this->checkRoutes($role, $route)) {
return true;
}
}
return false;
}
So, all of the above should get you more than going I'd say.
GL & HF
I have set up a many to one relation and want to add a new product-object linked to its category. Related to this I have 2 questions:
I get stuck on saving the category object to the new product. Tried different options and read related questions here. At this moment I am getting the error:
Attempted to call an undefined method named "getCategory" of class "AppBundle\Controller\ProductController".
I do have the method getCategory in my Product class.
What am I missing here?
Another thing I would like to know is do I need to pass the category-id in the url to get the related products for that category?
I have a category class:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="category")
*/
class Category
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $cat_id;
...
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category")
*/
private $products; ...
public function __construct()
{
$this->products = new ArrayCollection();
}
and product class:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\Category;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="product")
*/
class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $prd_id;
/**
* #var Category
*
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="cat_id", referencedColumnName="cat_id", nullable=false)
*/
private $category;
....
/**
* Set category
*
* #param \AppBundle\Entity\Category $category
*
* #return Product
*/
public function setCategory(\AppBundle\Entity\Category $category)
{
$this->category = $category;
return $this;
}
/**
* Get category
*
* #return \AppBundle\Entity\Category
*/
public function getCategory()
{
return $this->category;
}
From my list of categories "/categories" I link the categories to the productlist "/cat1/product" (<-- do I need to pass the category-id here?). There I want to add a new product and call the following action in my ProductController:
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use AppBundle\Entity\Product;
use AppBundle\Form\ProductType;
use Symfony\Component\HttpFoundation\Request;
class ProductController extends Controller
{
/**
* #Route("/cat{cat_id}/product/new", name="newproduct")
*/
public function newAction(Request $request, $cat_id)
{
$product = new Product();
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$category = $this->getCategory();
$product->setCategory($category);
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return $this->redirectToRoute('productlist');
}
return $this->render('product/new.html.twig', array(
'form' => $form->createView(),
'cat_id' => $cat_id,
));
}
Suggestions appreciated!
when you do :
$category = $this->getCategory();
$this represent your productController, it is the reason of the undefined method error. to get the category object you must do :
$categoryRepository = $this->getDoctrine()->getRepository('AppBundle:Category');
$product->setCategory($categoryRepository->find($cat_id));
hope that may help you.
i want to fetch the data of a menu including its categories in a custom repository by a dql-statement, but it doesn't return the associated entities. I need it as json data, so i added the hydration mode Query::HYDRATE_ARRAY to the function call.
/**
* #ORM\Entity(repositoryClass="Company\Repository\Doctrine\MenuRepository")
* #ORM\Table(name="menu")
*/
class Menu {
/**
* #var \Company\Entity\MenuCategory[]
*
* #ORM\OneToMany(targetEntity="Company\Entity\MenuCategory", mappedBy="menu")
*/
protected $categories;
}
/**
* #ORM\Entity(repositoryClass="Company\Repository\Doctrine\MenuCategoryRepository")
* #ORM\Table(name="menu_category")
*/
class MenuCategory {
/**
* #var \Company\Entity\Menu
*
* #ORM\ManyToOne(targetEntity="Company\Entity\Menu", inversedBy="categories")
* #ORM\JoinColumn(nullable=false)
*/
protected $menu;
}
class MenuRepository extends EntityRepository implements MenuRepositoryInterface {
public function findById($id, $hydration = Query::HYDRATE_OBJECT) {
$queryBuilder = $this->createQueryBuilder('menu')
->leftJoin('menu.categories', 'categories')
->where('menu.id = :menuId')
->setParameter('menuId', $id);
return $queryBuilder->getQuery()->getSingleResult($hydration);
}
}
The result looks like that:
array(4) {
["id"]=>int(1)
["name"]=> string(6) "Test"
}
I've a problem with my many-to-many relation. I want to have access to the reference table for a querybuilder query. With a many-to-many relation I don't have access to my reference table, so I've set up two one-to-many relationships. My structure look likes:
User ---> UserUserCategory <--- UserCategory
The above structure has two one-to-many relationships and are working fine with the database. When I have a user with the following data in the database (in UserUserCategory):
Table User
ID | Name
1 | Bart
2 | Gerard
Table Category
ID | Name
1 | Officer
2 | Medic
Table UserUserCategory
User | Category
1 | 1
2 | 2
So Bart is an Officer and Gerard is a Medic. But when I want to retrieve the data, it said that Bart is the Medic, and Gerard has a "null" value in the category.
My User-entity:
/**
* Entity Class representing a post of our User module.
*
* #ORM\Entity
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="User\Repository\UserRepository")
*
*/
class User extends zfcUser implements UserInterface
{
/**
* Categories from user
*
* #ORM\OneToMany(targetEntity="User\Entity\UserUserCategory", mappedBy="user_id", cascade={"remove", "persist"})
* #var UserUserCategory
* #access protected
*/
protected $user_usercategories;
//name & user_id comes here
/**
* Constructor to make a new ArrayCollection for addresses
*
*
*/
public function __construct()
{
$this->user_usercategories = new ArrayCollection();
}
/**
* #param Collection $categories
*/
public function addUserUserCategories(Collection $user_usercategories)
{
foreach ($user_usercategories as $user_usercategorie) {
$user_usercategorie->setUser($this);
$this->user_usercategories->add($user_usercategorie);
}
}
/**
* #param Collection $categories
*/
public function removeUserUserCategories(Collection $user_usercategories)
{
foreach ($user_usercategories as $user_usercategorie) {
$user_usercategorie->setUser(null);
$this->user_usercategories->removeElement($user_usercategorie);
}
}
/**
* #return Collection
*/
public function getUserUserCategories()
{
return $this->categories;
}
}
My UserCategory-entity:
/**
* A User category entity.
* #ORM\Entity
* #ORM\Table(uniqueConstraints={#ORM\UniqueConstraint(name="unique_name_parentId", columns={"name", "parent_id"})})
* #ORM\HasLifecycleCallbacks
*/
class UserCategory extends Category
{
/**
* User_usercategories
*
* #ORM\OneToMany(targetEntity="User\Entity\UserUserCategory", mappedBy="category_id")
* #var UserUserCategory
* #access protected
*/
protected $user_usercategories;
/**
* Constructor
*/
public function __construct()
{
$this->user_usercategories = new ArrayCollection();
}
/**
* #param Collection $categories
*/
public function addUserUserCategories(Collection $user_usercategories)
{
foreach ($user_usercategories as $user_usercategorie) {
$user_usercategorie->setCategory($this);
$this->user_usercategories->add($user_usercategorie);
}
}
/**
* #param Collection $categories
*/
public function removeUserUserCategories(Collection $user_usercategories)
{
foreach ($user_usercategories as $user_usercategorie) {
$user_usercategorie->setCategory(null);
$this->user_usercategories->removeElement($user_usercategorie);
}
}
/**
* #return Collection
*/
public function getUserUserCategories()
{
return $this->categories;
}
}
My UserUserCategory-entity:
/**
* Entity Class representing a post of our User_UserCategory entity.
*
* #ORM\Entity
* #ORM\Table(name="user_usercategory")
*
*/
class UserUserCategory
{
/**
* User with a category
*
* #ORM\ManyToOne(targetEntity="User\Entity\User", inversedBy="user_usercategories")
* #ORM\JoinColumn(name="user_id", referencedColumnName="user_id", nullable=false, onDelete="CASCADE")
* #ORM\Id
*
* #var User
* #access protected
*/
protected $user_id;
/**
* Category from user
*
* #ORM\ManyToOne(targetEntity="User\Entity\UserCategory", inversedBy="user_usercategories")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
* #ORM\Id
*
* #var Category
* #access protected
*/
protected $category_id;
public function getUser()
{
return $this->user;
}
/**
* Set User
*
* #param User $user
* #return User
*/
public function setUser(User $user = null)
{
//die('setUser');
$this->user = $user;
return $this;
}
public function getCategory()
{
return $this->category;
}
/**
* Set Category
*
* #param Category $category
* #return Category
*/
public function setCategory(Category $category = null)
{
$this->category = $category;
return $this;
}
}
When I execute the following line, it gives back the wrong result. The wrong category pops up:
\Doctrine\Common\Util\Debug::dump($this->getEntityManager()->find('User\Entity\User', '49')->user_usercategories);
die;
Result:
array(1) {
[0]=>
object(stdClass)#452 (3) {
["__CLASS__"]=>
string(28) "User\Entity\UserUserCategory"
["user_id"]=>
string(16) "User\Entity\User"
["category_id"]=>
string(24) "User\Entity\UserCategory"
}
}
In the category_id is the medic printed, I expect the officer to get back.
In my other user, (id=60) the category_id field is "null". So it look likes Doctrine skips the first input in my UserUserCategory, starts with the second and can't get the last category anymore.
No offence, but I find your code very hard to read. I would suggest you to do few corrections and that might even help you in solving the problem.
1: Naming: Instead of UserCategory, rename it to Category. If your Category will have different types, create new column "type" with values from constansts like
class Category
{
const TYPE_USER = 1 ;
....
2: Instead of addCategories(Collection $array), do singular version like
public function addCategory(Category $category)
{
$reference = new UserCategory() ;
$reference->setUser($this) ;
$reference->setCategory($category) ;
$this->user_categories->add($reference) ;
}
public function removeCategory(Category $category)
{
foreach($this->user_categories as $reference) {
if ( $reference->getCategory() === $category )
$this->user_categories->removeElement($reference) ;
}
}
Symfony2 automaticaly recognizes methods like this. Even if your relation is plural (like categories), s2 will find singularified addCategory and removeCategory methods.
To get array of categories, use this:
public function getCategories()
{
$categories = new ArrayCollection() ;
foreach($this->user_categories as $reference) {
$categories->add( $reference->getCategory() ) ;
}
return $categories ;
}
If you do it like this, you will probably solve the problem you have.
I'm trying to make a Field of a Object slugable.
Model looks like:
namespace myBundle\Bundles\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* myBundle\Bundles\BlogBundle\Entity\Category
*
* #ORM\Table()
* #ORM\Entity
*/
class Category
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string $title
*
* #Gedmo\Sluggable
* #ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* #Gedmo\Slug(separator="-", updatable=false, unique=true)
* #ORM\Column(name="slug", type="string", length=255, unique=true)
*/
private $slug;
// other properties and methods
The Fixtures:
namespace myBundle\Bundles\BlogBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use tooMuch\Bundles\BlogBundle\Entity\Category;
class LoadCategoryData extends AbstractFixture implements OrderedFixtureInterface
{
public function load($manager)
{
$this->generateCategory($manager);
}
public function generateCategory($manager)
{
for ($i=0; $i < 10; $i++) {
$category = new Category();
$category->setTitle('Category '.$i);
$manager->persist($category);
$manager->flush();
$this->addReference('category'.$i, $category);
unset($category);
}
}
schema create:
# sf doctrine:schema:create
ATTENTION: This operation should not be executed in a production environment.
Creating database schema...
Database schema created successfully!
but then when I'm trying to add the fixtures:
# sf doctrine:fixtures:load
> purging database
> loading myBundle\Bundles\BlogBundle\DataFixtures\ORM\LoadCategoryData
[PDOException]
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'slug' cannot be null
doctrine:fixtures:load [--fixtures[="..."]] [--append] [--em="…"]
#
any ideas?
The problem isn't in the fixtures, it's in Slug generation itself. If it's properly set up, you should never have $slug as null
Are you sure you have Sluggable listener attached?