in this tutorial is proposed this file system:
XXXBundle
|---Features
| |----FeaturesContext.php
|---ProductCategoryRelation.feature
where FeaturesContext.php is the file that stores the functions
//FeaturesContext.php
/**
* Feature context.
*/
class FeatureContext extends BehatContext
{
/**
* #Given /I have a category "([^"]*)"/
*/
public function iHaveACategory($name)
{
$category = new \Acme\DemoBundle\Entity\Category();
$category->setName($name);
...
And inside ProductCategoryRelation.feature is proposed to write the features and the scenarios:
Feature: Product Category Relationship
In order to setup a valid catalog
As a developer
I need a working relationship
This being the feature, we now need the scenarios to be defined.
Scenario: A category contains a product
Given I have a category "Underwear"
...
But if my app is growing up, how should refactor for example FeaturesContext.php?
You might use subcontexts to split step definitions into multiple context classes: http://docs.behat.org/guides/4.context.html#using-subcontexts
Related
I am working a custom bundle which should be used in different Symfony 5+ projects. The bundle includes some entities which inherit from the same Mapped Superclass:
/**
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\Table(name="vehicles")
* #ORM\DiscriminatorColumn(name="vehicle_type", type="string")
* #ORM\DiscriminatorMap({
* "race_car" = "RaceCar",
* "sailboat" = "Sailboat",
* ...})
*/
abstract class Vehicle {
/** #Column(type="string") */
protected $color;
// ... more fields and methods
}
/**
* #ORM\Entity
*/
class RaceCar extends Vehicle {
...
}
/**
* #ORM\Entity
*/
class SailBoat extends Vehicle {
...
}
This works fine and it is no problem to use a custom naming strategy (or completely custom type names) in the DiscriminatorMap.
Now I would like to add more Vehicle sub classes to a project which uses the bundle. Since it seems not to be possible to extend the DiscriminatorMap outside its original definition (is it?), I switched to not specifying a map manually but letting Doctrine build it automatically:
If no discriminator map is provided, then the map is generated
automatically. The automatically generated discriminator map contains
the lowercase short name of each class as key.
While this works in a new project, all existing entries in the database of an existing project already use the custom naming. This would conflict with the auto generated lowercase naming: e.g. race_car vs. racecar.
Is it possible to provide a custom naming strategy which is than be used by Doctrine to build the map?
Or do I have to manually update the database by replacing the old mapping names with the new (lowercase) ones?
How can i access the object of second table when joined (non-related tables)?
I have two table which are not related and I want to get the object of the second class (from below dump output)
My repository with dump
For example:
my controller:
$ProductSet_Repo = $em->getRepository('MyTestBundle:Product\ProductSet')->FindProductSet($productid);
Normally when the tables are related I can simple do
$productSet = $ProductSet_Repo->getproductid()->getProduct(); to get the object of Product class From ProductSet Class.
See My Dump
However since the tables are not in relationship and when i dump the data i get the objects of two classes is there a way I can access the Object My\TestBundle:Products\Entity\Product\ProductSet and \My\TestBundle\Entity\Product\Product?
Note: i don't want to do establish relationship between the two tables as I am working on already existing table for which i don't want to make any changes
Also I know I can select the fields which i want to retrieve. (I dont want to do that)
You write:
i don't want to do establish relationship between the two tables as I am working on already existing table for which i don't want to make any changes.
But with doctrine you are very well able to make a association between two entities without changing the tables. As far as I can see from your query you have a product_id column in your product_set table. That is all you need to make an association between Product and ProductSet.
In your ProductSet class you can do:
<?php
namespace My\TestBundle\Entity\Product;
class ProductSet
{
//... other properties
/**
* #var Product
* #ORM\Id
* #ORM\ManyToOne(targetEntity="My\TestBundle\Entity\Product\Product")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
protected $product;
/**
* Set the product.
*
* #param Product $product
* #return ProductSet
*/
public function setProduct(Product $product)
{
$this->product = $product;
return $this;
}
/**
* Get the product.
*
* #return Product
*/
public function getProduct()
{
return $this->product;
}
//... other setters and getters
}
Now you can do:
$repository = $em->getRepository('MyTestBundle:Product\ProductSet')
$productSets = $repository->findBy(array('product' => $productid));
foreach($productSets as $productSet){
$productSet->getProduct()->getId() === $productId; // true
}
You can still join them (despite of strange naming convention you have id of corresponding object in the other entity) using query builder or native sql, but it's a really bad way.
it was developed by previous webdeveloper and i dont want to spend more time as i work as free lancer
That's not an excuse. You should create a relation and migration for these data. Getting money for a poorly designed and developed app is not cool.
Probably additional work when working with that poor design will take your more time than doing it in a proper way.
I pass all the day reading, testing, re-reading and re-testing, I can't find a proper solution to my problem.
Let say you have a file entity, which switch type, go to a different block. (I mean, in the database i only have 1 file table, but in the display, i have a block "images", another "documents" and another "map"). All 3 are files with a different value for the field "type".
I know that the simplest should be to have 3 tables but i don't want to ruin my architecture because of using sonata.
I totally figure out how to do it in practice with preupdate and prepersist events and the initialisation phase. But even when i try to give the option "admin_code" to my unmapped property $images. It's always saying that it need an admin class. As the admin class is mapped by the annotation #ORM\OneToMany, i can't use it otherwise doctrine generates me another table.
Is there any way to achieve this ?
Is the admin_code option bugged ?
Or should i pass by a front-end solution? (i totally prefer back-end, but i will do with what is possible)
Did you try to use Doctrine inheritance
I think you'll be able to create an abstract File class and 3 other classes that extend File :
/**
* #ORM\Entity
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="type", type="string")
* #ORM\DiscriminatorMap({"image" = "Image", "document" = "Document", "map" = "Map"})
*/
abstract class File {
...
}
Then you'll have to create the 3 classes:
class Image extends File {
...
}
class Document extends File {
...
}
class Map extends File {
...
}
After, you'll be able to create 3 differents admin class to display in Sonata.
I'm having trouble finding a way to appropriately extend an Entity across bundles in Symfony2 using Doctrine2 as the ORM.
Currently there are three methods that I've found to extending entities in Symfony2 using Doctrine2 as the ORM. Mapped Superclass, Single Table Inheritance and Class Table Inheritance. None of these work for what I'm looking to do.
I have two Bundles: UserBundle and BlogBundle. I want to be able to use the UserBundle in projects that do not have the BlogBundle, but the BlogBundle will always be used in projects that have the User Bundle. It's ok if the BlogBundle has dependencies on the UserBundle, but not the other way around.
I have two entities:
BlogBundle\Entity\Post and
UserBundle\Entity\User
Relationship:
There needs to be a One to Many relationship between Users and Blog Posts. This is achieved through a Author_ID property (column) on the BlogBundle\Entity\Post object (table) which is mapped to UserBundle\Entity\User.id
The Problem:
I can call the UserBundle\Entity\User entity directly from within the BlogBundle and achieve what I'm looking for using a Uni-Directional mapping. This does not allow me to access all posts by a user from within a User object. I can access the data via custom queries but this is not as clean as accessing posts by a user through the user object.
What I'd like to do is extend the UserBundle\Entity\User object from within the BlogBundle, and add the methods and properties to this object that establish the One to Many mapping used within the BlogBundle. None of this is persisted, it simply defines the relationship and allows me to logically access all posts created by a user in an application that implements both the BlogBundle and UserBundle by adding needed functionality to the User object within the blog bundle (thus avoiding a dependency from the UserBundle to the BlogBundle).
When I create a BlogBundle\Entity\User object and extend UserBundle\Entity\User I must declare #ORM\Table(name="usertablename"). If I don't, any attempt to access the BlogBundle\Entity\User object will fail to access the database. Since none of the additions in the extended object persist, this works fine across bundles. The issue with this is when I call "php app/console doctrine:schema:update --force", there is a conflict since two entities try to map to & create the same table. I have tried using the ResolveTargetEntityListener feature that was recently implemented but this, along with Mapped Superclas, STI and CTI all force a dependency on the BlogBundle from the UserBundle.
Below are my objects to help illustrate my my setup. They have been abbreviated for clarity. I realize some of the semantics aren't correct but it's intended to communicate the ideas & configuration.
UserBundle\Entity\User
#ORM\Table(name="app_user")
#ORM\Entity
class User implements UserInterface
{
...
}
BlogBundle\Entity\Post
#ORM\Table(name="app_post")
#ORM\Entity
class Post
{
...
#ORM\Column(name="author_id", type="integer")
protected $author_id;
#ORM\ManyToOne(targetEntity="\App\BlogBundle\Entity\User", inversedBy="posts")
#ORM\JoinColumn(name="author_id", referencedColumnName="id")
protected $author;
}
BlogBundle\Entity\User
use App\UserBundle\Entity\User as BaseUser
#ORM\Entity
#ORM\table(name="app_user")
class User extends BaseUser
{
....
#ORM\OneToMany(targetEntity="App\BlogBundle\Entity\Post", mappedBy="author")
protected $posts;
public function __construct()
{
parent::_construct();
$this->posts = new \Doctrine\Common\Collections\ArrayCollection();
}
....
/* Getters & Setters, nothing that defines #ORM\Column, nothing persisted */
}
This works but the problem is that I'm mapping two entities in the project to the same table. The extended object doesn't grab the #ORM\Table(name="app_user") from it's parent so it must be defined in BlogBundle\Entity\User. If not any reference to this object from a controller will not access the database. Since nothing is persisted from the extended object nothing is broken except for when I try to update the database schema from the console.
I can use a unidirectional relationship, but this limits how I can access the data from within a controller.
You can see in this link to know about inheritance: http://docs.doctrine-project.org/en/latest/reference/inheritance-mapping.html#single-table-inheritance
You must declare in UserBundle\Entity\User:
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"baseuser" = "UserBundle\Entity\User", "blogUser" = "BlogBundle\Entity\User"})
*/
class User implements UserInterface
{
...
}
And BlogBundle\Entity\User
use App\UserBundle\Entity\User as BaseUser;
/**
* #ORM\Entity
*/
class User extends BaseUser
{
....
}
Goodluck!
I think you could find this Bundle interesting:
https://github.com/mmoreram/SimpleDoctrineMapping
It allows you to define by parameters what files are mapping your entities, allowing to override every entity of your generic bundles.
For example:
parameters:
#
# Mapping information
#
test_bundle.entity.user.class: "TestBundle\Entity\User"
test_bundle.entity.user.mapping_file_path: "#TestBundle/Mapping/Class.orm.yml"
test_bundle.entity.user.entity_manager: default
test_bundle.entity.user.enable: true
The only contra I see is that you have to define all the next entities the same way cause you disabled auto_mapping...
I'm building a small website with Symfony2 and Doctrine2. There are blog posts, events and press releases. Each of these is so similar that I decided to use single table inheritance (STI) with a parent table called 'Node'.
Nodes have:
a 'published' field, which is boolean
a 'locale' field, which is a string and says 'this is only to be shown in this locale'. (The locale is passed in via the request).
By default, I only want to display published nodes that are from the current locale.
Obviously, I could create lots of queries in the repository that look something like:
$this->createQueryBuilder('Event')
->where('Node.published = 1')
->where('Node.locale = :locale')
but this doesn't seem very DRY.
So how do I build a default query which other queries can 'inherit' from? This should include default Doctrine queries based on relations.
Inheritance is probably overkill.
Why not just create a little factory method that gives you a preconfigured queryBuilder?
class NodeRepository extends Doctrine\ORM\EntityRepository {
public function getLocalizedNodeQueryBuilder($type,$locale){
return $this->getQueryBuilder($type)
->where('Node.published = 1')
->where('Node.locale = :locale')
->setParameter('locale',$locale);
}
}
You could do the same thing, and simply override getQueryBuilder if you're sure you always want your querybuilder configured that way.
You don't need to build anything like that into your repository classes. If you set up single table inheritance with a "Discriminator Map" you'll end up with seperate classes (Entities). Doctrine will take care of filtering by your "node type" when it interacts with the DBAL.
http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/inheritance-mapping.html#single-table-inheritance
For example..
namespace MyProject\Model;
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"node" = "Node", "blogpost" = "Blogpost", "events" = "Events"})
*/
class Node
{
// ...
}
/**
* #Entity
*/
class Blogpost extends Node
{
// ...
}
/**
* #Entity
*/
class Events extends Node
{
// ...
}
// get me all the blogposts
$blogposts = $em->getRepository('Blogpost')->findAll();
// get me all the events
$events = $em->getRepository('Events')->findAll();
This is especially beneficially as you'll be able to encapsulate your "Blogpost" logic into its own entity, rather than trying to represent it with its parent class "Node".