Doctrine entity to different json views - serialization

When I get a list of entities from Doctrine, how can I return different json 'views' ?
Ie. when I load Student classes I want views with only id, displayName and another with more details id, firstName, lastName, email, groups.
Groups is another collection with id, name.
I'm using lumen and doctrine.

You can implement JsonSerializable interface on your entityes and create custom versions on it.
ie:
class MyEntity implements JsonSerializable {
/**
* Doctrine annotation
*/
private $myEntityMember;
...
public function jsonSerialize() {
//serialize your entity here and use custom params to modify the serialized object output
}
}
This way you can call $myEntityObject->jsonSerialize() whenever you need.

Related

Change column name for non-primitive field (navigation) with EF Core

Given the following classes:
public class Mission
{
private MissionCard _missionCard;
}
public class MissionCard
{
}
I would like to create this relationship via Fluent API so that _missionCard is treated as a relationship and can be populated from DB but isn't available as a property on my Mission model.
I can create that relationship with:
modelBuilder.Entity<Mission>().HasOne<MissionCard>("_missionCard");
but by default this creates a FK column named "_missionCard". The docs show that a custom name can be specified when using .Property("property name").FromField("field name") but you cannot use .Property for non-primitive types.
Is it possible to change the column name for a relationship like above?
Managed to resolve this by inverting the relationship:
modelBuilder.Entity<MissionCard>()
.HasMany<Mission>()
.WithOne("_missionCard")
.HasForeignKey(nameof(MissionCard));

Mapping an extension table that might not have a row

I'm working with Fluent nHibernate on a legacy database and have a main Person table and several extension tables containing additional information about the person. These extension tables are one-to-one, meaning that a person will only have one row on the extension table and the extension table should always map back to one person.
Table: Person
Columns: PersonID, FirstName, LastName, etc.
Table: PersonLogin
Columns: PersonID (FK, unique), UserName, Password, etc.
I have my mappings defined as this (with the irrelevant properties omitted):
public PersonMap()
{
Table("Person");
Id(x => x.Id, "PersonID").Not.Nullable();
References(x => x.Login, "PersonID").LazyLoad();
}
public LoginMap()
{
Table("PersonLogin");
Id(x => x.Id, "PersonID").GeneratedBy.Foreign("Person");
References(x => x.Person, "PersonID").LazyLoad();
}
This works when I have data on both tables, but I recently learned that some of the extension tables don't have data for all Person rows. This caused me to get errors during the query. So, I added .NotFound.Ignore() to my PersonMap making it look like this:
References(x => x.Login, "PersonID").LazyLoad().NotFound.Ignore();
That caused me to get unnecessary selects from the Login table due to https://nhibernate.jira.com/browse/NH-1001 when my business layer doesn't need to project any of the extension table values. It is causing the performance to be terrible in some of my search queries.
I've scoured a lot of posts, but haven't found a rock solid answer about how to address this scenario. Below are the options I've tried:
Option One:
Create rows on the extension table to ensure there is no Person without a row on the extension table and then remove the .NotFound.Ignore().
The issue with this option is that it's a legacy database and I'm not sure where I'd need to update to ensure that a PersonLogin is inserted when a Person is inserted.
Option Two:
Remove the PersonLogin reference from my PersonMap and custom load it inside my Person class. Like this:
public class Person
{
/// <summary> Gets or sets the PersonID </summary>
public virtual int Id { get; set; }
private bool loadedLogin;
private PersonLogin login;
public virtual PersonLogin Login
{
get
{
if (!loadedLogin)
{
login = SessionManager.Session().Get<PersonLogin>(Id);
loadedLogin = true;
}
return login;
}
set
{
login = value;
loadedLogin = true;
}
}
}
The issue I'm having with it is that I can't eagerly fetch the data when performing a query to pull back a large number of Person objects and their Logins.
Option Three:
I just started playing to see if I could write a custom IEntityNotFoundDelegate to not throw the exception for these objects.
private class CustomEntityNotFoundDelegate : IEntityNotFoundDelegate
{
public void HandleEntityNotFound(string entityName, object id)
{
if (entityName == "my.namespace.PersonLogin")
{
return;
}
else
{
throw new ObjectNotFoundException(id, entityName);
}
}
}
And I added this to the config
cfg.EntityNotFoundDelegate = new CustomEntityNotFoundDelegate();
It catches my scenario and returns back now instead of throwing the error, but now when I try to project those PersonLogin properties onto my business objects, it's attempting to use the Proxy object and throws this error that I'm trying to figure out if I can handle cleanly (possibly in a IPostLoadEventListener).
System.Reflection.TargetException occurred
Message = Non-static method requires a target
I think I've got this working now by keeping the .NotFound.Ignore().
I originally stated:
That caused me to get unnecessary selects from the Login table due to https://nhibernate.jira.com/browse/NH-1001 when my business layer doesn't need to project any of the extension table values. It is causing the performance to be terrible in some of my search queries.
I was able to tweak my LINQ queries to use the IQueryOver in some instances and to improve my use of LINQ in other scenarios to project only the necessary values. This appears to have resolved the queries from pulling back the extension tables since their values were not needed in the projections.
I thought that my queries weren't projecting these extension tables, but figured out that I had a method ToKeyValuePair that I was using in the projection to concatenate the ID and a Name field together of some related properties. That method was causing the objects to load completely since LINQ wasn't able to determine that the needed fields were present without joining to the extension table.

How to get Doctrine2 entity identifier without knowing its name

I am attempting to create an abstracted getId method on my base Entity class in Symfony2 using Doctrine2 for a database where primary keys are named inconsistently across tables.
When inspecting entity objects I see there is a private '_identifier' property that contains the information I am trying to retrieve but I am not sure how to properly access it.
I'm assuming there is some simple Doctrine magic similar to:
public function getId()
{
return $this->getIdentifier();
}
But I haven't managed to find it on the intertubes anywhere.
You can access this information via EntityManager#getClassMetadata(). An example would look like this:
// $em instanceof EntityManager
$meta = $em->getClassMetadata(get_class($entity));
$identifier = $meta->getSingleIdentifierFieldName();
If your entity has a composite primary key, you'll need to use $meta->getIdentifierFieldNames() instead. Of course, using this method, you'll need access to an instance of EntityManager, so this code is usually placed in a custom repository rather than in the entity itself.
Hope that helps.

Mapping two tables to one entity in Doctrine2

I'm looking at using doctrine for an application I'm working on - but after reading the documentation I'm having trouble conceptualizing how to represent the database structure we have in terms of entities.
I have many tables which have partner tables which hold translation data like the following....
Where I would like to have one Entity (Navigation Element) which had access to the 'label' field depending on what Language I set in my application. The following from the Doctrine documentation seems to suggest that you need to define one (single) table which is used to persist an entity
http://www.doctrine-project.org/docs/orm/2.0/en/reference/basic-mapping.html
By default, the entity will be
persisted to a table with the same
name as the class name. In order to
change that, you can use the #Table
annotation as follows:
Or do I need to define two entities and link them (or allow the translation table to inherit from the element table).
And what strategy would I use to always insert a language_id clause to the Join (to ensure I'm pulling the right label for the currently set language). Is this something I would define in the entity itself, or elsewhere?
This seems to suit a One-To-Many Bidirectional association. This is the scenario from that page translated to your situation:
/** #Entity */
class NavigationElement
{
// ...
/**
* #OneToMany(targetEntity="NavigationElementTranslation", mappedBy="navigationElement")
*/
private $translations;
// ...
public function __construct() {
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
}
}
/** #Entity */
class NavigationElementTranslation
{
// ...
/**
* #ManyToOne(targetEntity="NavigationElement", inversedBy="translations")
* #JoinColumn(name="navigation_element_id", referencedColumnName="id")
*/
private $navigationElement;
// ...
}
You could add a getLabel($languageId) method to the NavigationElement entity that searches through the translations to get the correct label:
public function getLabel($languageId) {
foreach($this->translations as $trans) {
if($trans->languageId == $languageId)
return $trans->label;
}
throw new InvalidArgumentException();
}
And you could use the following DQL to ensure you only load the translation you want into the $translations property:
$query = $em->createQuery(
"SELECT ne, net
FROM Entity\NavigationElement ne
JOIN ne.translations net WITH net.languageId = :langId"
);
$query->setParameter('langId', $languageId);
$navigationElements = $query->execute();
This situation sounds like one where you would want to cache aggressively. Make sure you look into Doctrine 2's caching mechanisms too.
Also, internationalization can be handled reasonably well in PHP with gettext if you find join tables for translations start to become unmanageable.
I would also direct anyone who has to tackle this same problem to take a look at the following doctrine extension.
http://www.gediminasm.org/article/translatable-behavior-extension-for-doctrine-2

nhibernate - disable automatic\lazy loading of child records for one to many relationsihps

I would like to know if there is a way to disable automatic loading of child records in nHibernate ( for one:many relationships ).
We can easily switch off lazy loading on properties but what I want is to disable any kind of automatic loading ( lazy and non lazy both ). I only want to load data via query ( i.e. HQL or Criteria )
I would still like to define the relationship between parent child records in the mapping file to facilitate HQL and be able to join parent child entities, but I do not want the child records to be loaded as part of the parent record unless a query on the parent record
explicitly states that ( via eager fetch, etc ).
Example:
Fetching Department record from the database should not fetch all employee records from the database because it may never be needed.
One option here is to set the Employees collection on Department as lazy load. The problem with this approach is that once the object is given to the calling API it can 'touch' the lazy load property and that will fetch the entire list from the db.
I tried to use 'evict' - to disconnect the object but it does not seem to be working at all times and does not do a deep evict on the object.
Plus it abstracts the lazy loaded property type with a proxy class that plays havoc later in the code where we are trying to operate on the object via reflection and it encounters unexpended type on the object.
I am a beginner to nHibernate, any pointers or help would be of great help.
Given your request, you could simply not map from Department to Employees, nor have an Employees property on your department. This would mean you always have to make a database hit to find the employees of a database.
Aplogies if these code examples don't work out of the box, I'm not near a compiler at the moment
So, your department class might look like:
public class Department
{
public int Id { get; protected set; }
public string Name { get; set; }
/* Equality and GetHashCode here */
}
and your Employee would look like:
public class Employee
{
public int Id { get; protected set; }
public Name Name { get; set; }
public Department Department { get; set; }
/* Equality and GetHashCode here */
}
Any time you wanted to find Employees for a department, you've have to call:
/*...*/
session.CreateCriteria(typeof(Employee))
.Add(Restrictions.Eq("Department", department)
.List<Employee>();
Simply because your spec says "Departments have many Employees", doesn't mean you have to map it as a bi-directional association. If you can keep your associated uni-directional, you can really get your data-access to fly too.
Google "Domain Driven Design" Aggregate, or see Page 125 of Eric Evan's book on Domain Driven Design for more information
You can have the lazy attribute on the collection. In your example, Department has n employees, if lazy is enabled, the employees will not be loaded by default when you load a department : http://www.nhforge.org/doc/nh/en/#collections-lazy
You can have queries that explicitly load department AND employees together. It's the "fetch" option : http://www.nhforge.org/doc/nh/en/#performance-fetching-lazy