The question hard to formulate in just one sentence when I don't have the exact terms ready for use, but I'm basically working on a Symfony 5 project that involves a MySQL database. I use Twig to allow for communication between my PHP controllers and my HTML interface. Until now, I've been doing just fine using simple references to entity fields in Twig, such as:
myEntity.someField
To get the value I needed. However, I currently need to reference a "nested" field like so :
myEntity1.myEntity2Field.someField
The "nesting" making a world of a difference between the two. I am now getting an error when trying to do this (Impossible to access an attribute ("someField") on a string variable ("<value from entity2 field>")),
probably because my database is not organized correctly yet, from what I understand. Hopefully you could understand my difficulty. So, how can I tweak my database to allow this sort of double-referencing to take place?
Note: myEntity2Field refers to the name of a field from Entity2 that should serve as a reference to the entire Entity2 table, from which someField can then be extracted.
the error you are getting is because you are trying to access a value from a string or integer, not an Object.
myEntity.someField is the same as $myEntity->getSomeField() if you have a getter Method or $myEntity->someField if you are accessing the property directly.
If someField value is not an Object you can only access the value but if its an Object you will be able to access the other object properties and methods in this way.
To do so the best way is to use association(relation) between your entities spically if you have the association already in the database and if you build your relations right in your database so you jaut have to refernce this relation or association between your entities and doctrine will do the rest for you.
as an example:
<?php
/** #Entity */
class User
{
// ...
/**
* #ManyToOne(targetEntity="Address")
* #JoinColumn(name="address_id", referencedColumnName="id")
*/
private $address;
}
/** #Entity */
class Address
{
private $street;
public function getStreet(){
....
}
}
this example is a ManyToOne relation between the user and the addresse entity with the getters methos in the user entitiy you will be able to use the nesting when you access the user:
$user->getAddress()->getStreet()
as an example and this will work in twig also:
{{user.address.street}}
https://www.doctrine-project.org/projects/doctrine-orm/en/2.7/reference/association-mapping.html#many-to-one-unidirectional
the other way you can get the object inside your calss and map it with methods so you can access it as you want.
i hope i will be able to help you with this info.
Related
Is there a way to override the default behavior of SS Data Objects such that when I assign a static $table_name property to my DataObject the dev/build does not create a table name with the DO name like it normally does?
For example I have this very small Data Object
<?php
class SalesRep extends DataObject {
private static $table_name = 'tbl_users';
}
I am trying to prevent creation of table salesrep on dev/build and also I would like the ORM to know that when I do a $Model->write(); I'm writing to the table tbl_users instead of table salesrep
This is currently not possible with SilverStripe 3.x. SilverStripe uses the "convention over configuration" principle and the database tables always have the same name as the related DataObject.
However, in SS4, with namespacing, you'll be able to define a tablename in your config. As #bummzack already noted, this is currently in alpha.
However, you might try and overwrite DataObject's getBaseTable(), which method like:
/**
* Get the name of the base table for this object
*/
public function baseTable() {
return 'tbl_users';
}
but i doubt it'll work without problems, cause in other places the baseTable property is - again - generated out of the class names.
This is part of using the ORM that is within SilverStripe and can take some getting used to. I would perhaps look at this in two different ways...
1) If your goal is to present a certain name to the user, but have a different table name then the solution is to use singular_name and plural_name and then you are free to name the DataObject however you wish...
class tbl_users extends DataObject {
private static $singular_name = 'Sales Rep';
private static $plural_name = 'Sales Reps';
...
}
..remember the whole point of the ORM is that the PHP class defines the table and it would make sense to keep the table name the same as you'd like to use in the code.
2) If it absolutely has to be a specific table then you can specify it as an external table/content and one of the following solutions might suit you best... "Save to external Table", "External Content Module" or "External Data Module"
What I am trying to achieve
Users would be able to configure Doctrine entities through an HTML form on a website.
Users would be able to define new entities, as well as add and delete fields for existing entities. (Similar to Drupal's content types)
The Doctrine entities would get dynamic properties based on the configuration that the user supplied through the web UI.
Either the single DB table per Doctrine entity would be altered dynamically whenever an entity configuration changes; Or there could be multiple tables used per single entity (each new entity field would get its own table).
Done so far
I have been researching this for the past few days without much success but I stumbled across this answer which seems quite related to what I am trying to achieve.
I have registered and added the loadClassMetadata listener which maps the field foo:
// src/DynamicMappingTest/AdminBundle/EventListener/MappingListener.php
namespace DynamicMappingTest\AdminBundle\EventListener;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class MappingListener
{
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
if ($classMetadata->getName() != 'DynamicMappingTest\\AdminBundle\\Entity\\CustomNode')
{
// Not the CustomNode test class. Do not alter the class metadata.
return;
}
$table = $classMetadata->table;
$oldName = $table['name']; // ... or $classMetaData->getTableName()
// your logic here ...
$table['name'] = 'custom_node';
$classMetadata->setPrimaryTable($table);
$reflClass = $classMetadata->getReflectionClass();
dump($reflClass);
// ... or add a field-mapping like this
$fieldMapping = array(
'fieldName' => 'foo',
'type' => 'string',
'length' => 255
);
$classMetadata->mapField($fieldMapping);
}
}
Now, this all works as long as I have the foo property declared in the DynamicMappingTest\AdminBundle\Entity\CustomNode class:
// src/DynamicMappingTest/AdminBundle/Entity/CustomNode.php
namespace DynamicMappingTest\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* CustomNode
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="DynamicMappingTest\AdminBundle\Entity\CustomNodeRepository")
*/
class CustomNode
{
...
private $foo;
}
Problem
However, there is no way for me to know what properties the users will define for their custom entities. If I remove the foo property from the CustomNode class, the ReflectionClass that I get from the ClassMetadata will naturally not include the foo property and so I get the following exception whenever the mapField() in MappingListener is executed:
ReflectionException: Property DynamicMappingTest\AdminBundle\Entity\CustomNode::$foo does not exist
in vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php at line 80
77. */
78. public function getAccessibleProperty($class, $property)
79. {
80. $reflectionProperty = new ReflectionProperty($class, $property);
81.
82. if ($reflectionProperty->isPublic()) {
83. $reflectionProperty = new RuntimePublicReflectionProperty($class, $property);
Questions
Is it possible to have fully configurable dynamic Doctrine entities?
Am I on the right track with my approach? If not, could you suggest an alternative?
How could I have truly dynamic class properties? Or should I be generating new Doctrine entity PHP classes whenever the users change the entity configuration?
Is it possible to have fully configurable dynamic Doctrine entities?
Doctrine generates proxy classes for you entities. That means that doctrine generates PHP code with class, which extends your Entity class and overrides the methods - puts some custom logic and then calls the parent method.
So, I think that the only way to make this really happen is to generate the PHP code for entities in your code. That is, every time entity is created in your website, you should generate PHP file with that entity, then run migrations.
Am I on the right track with my approach? If not, could you suggest an alternative?
I don't think that you should use Doctrine ORM at all in this case, at least in the way you're trying to do that.
Generally, ORM is used for easier/more manageable programming. That is, you can set relations, use lazy-loading, unit of work (change entity properties and then just flush) etc. If your entities are generated dynamically, what features will you use at all? Developer will not write code for these entities, because, as you've said, there is no way to know what fields it will have.
You haven't provided concrete use-case - why do you want to do that in the first place. But I imagine that it could be really done in some easier way.
If users can store any structure at all, should you use MySQL at all? ElasticSearch or similar solutions could be really much better in such cases.
How could I have truly dynamic class properties? Or should I be generating new Doctrine entity PHP classes whenever the users change the entity configuration?
As I've mentioned - yes. Unless you would want to override or replace some of Doctrine code, but I imagine it could be lots of it (proxy classes etc.)
A user fills in the form and submits it. Based on the input, an object Organization is hydrated. I want to separate communication with database from the actual object.
I thought of creating an OrganizationMapper that holds the methods for database communication (save, delete...). The organization class would get the OrganizationMapper through the constructor.
With these class definitions, however, I can't instantiate the classes because of their mutual dependence.
How else could I separate the database communication from Organization and put it into OrganizationMapper?
class Organization
{
protected $id;
protected $name;
... other properties ...
public function __construct(OrganizationMapper $mapper)
{
$this->mapper = $mapper;
}
public function getId() {...}
public function setId($id) {...}
... other methods ...
public function saveToDb()
{
$this->mapper->save($this);
}
The OrganizationMapper is
class OrganizationMapper
{
public function __construct(Organization $organization)
{
$this->organization = $organization
}
... other methods
public function save($organization)
{... the code to use the methods of Organization class to save the data to the database...}
}
And that's why circular dependencies are usually considered a bad thing.
Kidding aside, it seems to me that you do not actually need the constructor dependency in the OrganizationMapper class. From the looks of it, you're passing the Organization instance that you want to persist as a parameter into the mapper's save() method anyway and shouldn't need the instance attribute $this->organization in that class at all.
In general, I'd try to keep the OrganizationMapper stateless. Try to avoid storing an Organization instance as an instance attribute (especially if you actually use that same mapper instance for persisting multiple Organizations). Just do as you already did with the save() method and pass the Organization object as a method parameter.
Also, I would not associate the Organization class with the mapper. One could argue that this violates the Single Responsibility Principle as it's not the class' responsibility to persist itself. You could move this logic to the calling code and have the Organization class not know about the mapper at all (which is nice, because you completely eliminate the circular dependency between the two classes):
class Organization
{
protected $id;
protected $name;
// <other properties here>
// <getters and setters here>
}
class OrganizationMapper
{
public function save(Organization $organization)
{
// save $organization to DB, somehow
}
}
$organization = new Organization();
$organization->setName('Foobar International Inc.');
$mapper = new OrganizationMapper();
$mapper->save($organization);
To find a better way of seperating these two concerns, think about the purposes of your two objects:
an Organization is there to give you access to all informations of an organization
your OrganizationMapper is there to save a Organization object to database.
When you think about it like this, then there's a couple of questions, that rise up:
Why does your Organization need a saveToDb() method? It's not it's job to save it?
An instance of OrganizationMapper should be able to save any Organization in the database, so why do you pass it in twice? (once in the constructor, and once in the save($organization) method). In that case - what happens, if you pass a different organization to the constructor than to the save method?
In your current example, how would you load an Organization from Database?
As alternative, I would suggest to remove saveToDb() from Organization entirely, as it's not the job of the org to save itself to database. Additionally, I would remove the current Constructor from OrganizationMapper. In it's current design, there's little reason to pass the Organization to the constructor.
Also, I would rename the OrganizationMapper to OrganizationRepository or OrganizationService. The primary purpose of that class is not to map SQL to Objects but to retrieve/save Organizations from/to DB. (Also, in OOP, classes should only follow the single responsibility pattern, so maybe the part mapping between SQL and Objects should happen in specializied class)
As a side note: generally, it's not a great idea, to give many ways to do exactly the same thing (e.g. saving an organization). This will probably just cause inconsistencies over time (consider that you will be adding some validation logic in the future, but might forget to also add it in the second place).
I hope this helps you :)
Disclaimer: I name your Organization type as OrganizationEntity in this post.
Pretty simply, it's the other way around.
The OrganisationMapper gets an OrganisationEntity object and persists it to wherever you want to, by means you can choose.
For your problem:
move the saveToDb() method from your OrganisationEntity to the OrganisationMapper and pass it an object to be saved.
I don't know why Mapper should do any opperations on DB? Mapper sounds like converting Entity (Organization) into something that can be an input for DB operation ie. Query Object.
You should rename your class into DAO or Repository. It would be better name.
IMHO, the best idea would be to have:
Organization as an object that holds domain logic
OrganizationMapper should convert your domain object into some kind of query object
OrganizationDao should take Organization as an input param and use OrganizationMapper to convert it and do operation on DB.
BTW, why you are not using some kind of an ORM like Doctrine for example? It would make your life easier :)
You can't do that in php. Imagine if it would be posibble. Then instance of Organization would have a property OrganizationMapper, which would have a property Organization. So, property of a property of an instance of the class would be the instance itself! It is only possible in languages with pointers like c++. So, I see only 2 solutions here:
Put the classes together
Have a single link (maybe have 1 class that calls another while second doesn't call first.)
I'm using PHP 5.4. I'm in the process of trying to make my application more SOLID. I'm currently going through my objects and making sure they follow SRP. I'm stuck on how to handle populating my object with properties, especially properties that "extend" the object's properties. Let me better explain.
I have a class called Flock (yes, a group of chickens--I'm in agriculture). It consists of several properties that are in the flocks table in the database: id, member_id, integrator_id, date_placed, date_picked_up, etc:
<?php
class Flock
{
private $id;
private $member_id;
private $farm_id;
private $integrator_id;
private $comments;
private $production_sq_ft;
private $number_of_houses;
private $avg_effective_age_of_houses;
private $date_placed;
private $date_picked_up;
private $head_started;
private $head_picked_up;
private $pounds_picked_up;
private $pounds_sold;
private $base_rate;
private $performance_rate;
private $fuel_rate;
private $other_rate;
private $feed_pounds_consumed;
public function __construct() {}
//Then a bunch of getters and setters.
}
My goal is to use the same object for both creating and retrieving a flock. However, in my database, I have created several "master" views--views that join together all lookup tables and perform any basic calculations on the data that can be performed at the row level. In this example, in my master view for flocks, I have joined the integrators table with my flocks giving me an integrator_name column in my view. The same goes for the members and farms tables. I have also found the difference in days between date_placed and date_picked_up giving me a column called total_time_in_facility. In my current version of this application, all this data is simply stored in an array and accessed using magic methods (__get). So if I wanted to get the integrator name, I would simply use $flock->integrator_name. However, if I were to use my Flock class to populate a database, I would not pass the integrator name--I would simply pass the values I've listed above.
My question is--what is the best way to get this extended data about a flock? Should I use another class to handle this (like FlockDetails)? Should I not be using this master view at all? Instead of performing any calculations in my view, should I simply be performing those within the class itself? I was under the impression that I should only be using a single class to describe a flock. If I were to create a new flock, I would use this class. If I wanted to retrieve on, I would simply populate it using a factory. But if I populate it using a factory, what about the additional details contained in my master view? Can anyone help me clear this up? Thanks!
Well, I would probably have a class Flock which represents a single row in the table "flocks" and class FlockEx which extends the class Flock by adding the fields you get as a result of the calculation in db. So, when you want to update or add a new flock to the db you use Flock class and when you retrieve flock fields along with the calculated ones you use FlockEx. Makes sense?
With these classes, how would you change a record for a "Person" to an "Employee".
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #DiscriminatorColumn(name="discr", type="string")
* #DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
*/
class Person
{
// ...
}
/**
* #Entity
*/
class Employee extends Person
{
// ...
}
I tried changing the value of the discriminator column but I can't access that. I also tried creating an 'Employee' instance and manually copy the data over but that doesn't work with auto-incrementing id's. It just gets added as a new record instead of updating the existing one.
Do I need to write a custom sql query or am I doing something else that is fundamentally wrong?
It is not a good sign when the type of an instance of an object needs to change over time. I'm not talking about downcasting/upcasting here, but about the need to change the real type of an object.
First of all, let me tell you why it is a bad idea:
A subclass might define more attributes and do some additionnal work
in it's constructor. Should we run the new constructor again? What
if it overwrites some of our old object's attributes?
What if you were working on an instance of that Person in some part of your code, and then it suddenly transforms into an Employee (which might have some redefined behavior you wouldn't expect)?!
That is part of the reason why most languages will not allow you to change the real class type of an object during execution (and memory, of course, but I don't want to get into details). Some let you do that (sometimes in twisted ways, e.g. the JVM), but it's really not good practice!
More often than not, the need to do so lies in bad object-oriented design decisions.
For those reasons, Doctrine will not allow you to change the type of your entity object. Of course, you could write plain SQL (at the end of this post - but please read through!) to do the change anyway, but here's two "clean" options I would suggest:
I realize you've already said the first option wasn't an option but I spent a while writing down this post so I feel like I should make it as complete as possible for future reference.
Whenever you need to "change the type" from Person to Employee, create a new instance of the Employee and copy the data you want to copy over from the old Person object to the Employee object. Don't forget to remove the old entity and to persist the new one.
Use composition instead of inheritance (see this wiki article for details and links to other articles). EDIT: For the hell of it, here's a part of a nice conversation with Erich Gamma about "Composition over Inheritance"!
See related discussions here and here.
Now, here is the plain SQL method I was talking about earlier - I hope you won't need to use it!
Make sure your query is sanitized (as the query will be executed without any verification).
$query = "UPDATE TABLE_NAME_HERE SET discr = 'employee' WHERE id = ".$entity->getId();
$entity_manager->getConnection()->exec( $query );
Here is the documentation and code for the exec method which is in the DBAL\Connection class (for your information):
/**
* Execute an SQL statement and return the number of affected rows.
*
* #param string $statement
* #return integer The number of affected rows.
*/
public function exec($statement)
{
$this->connect();
return $this->_conn->exec($statement);
}