According to the documentation, api-platform will eager-load related resources by default.
But on the default configuration, all my queries to resources with relations (mostly with typical Many2One
relationships) populate these properties with the object IRI instead of the serialized object.
E.g., for this entity:
/**
* @ORM\Entity(repositoryClass="App\Repository\BoostLeadContactActionRepository")
* @ORM\Table(name="BoostLeadContactActions")
*/
class BoostLeadContactAction {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\BoostLead", inversedBy="contacts")
* @ORM\JoinColumn(nullable=false)
*/
private $boostLead;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\ContactChannel", fetch="EAGER")
* @ORM\JoinColumn(nullable=false, referencedColumnName="Id")
*/
private $channel;
// getters/setters/extra properties removed for brevity
}
Then we have the corresponding ContactChannel
:
/**
* ContactChannel
*
* @ORM\Table(name="ContactChannels")
* @ORM\Entity
*/
class ContactChannel {
/**
* @var int
*
* @ORM\Column(name="Id", type="smallint", nullable=false, options={"unsigned"=true})
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="Name", type="string", length=64, nullable=false)
*/
private $name = '';
}
I've set up fetch="EAGER"
on the relationship even if in theory is not needed. My configuration is the default, but just in case I actually wrote this in my api_platform.yaml
:
eager_loading:
# To enable or disable eager loading.
enabled: true
# Fetch only partial data according to serialization groups.
# If enabled, Doctrine ORM entities will not work as expected if any of the other fields are used.
fetch_partial: false
# Max number of joined relations before EagerLoading throws a RuntimeException.
max_joins: 30
# Force join on every relation.
# If disabled, it will only join relations having the EAGER fetch mode.
force_eager: true
The result of debug:config api_platform
confirms the correct configuration is applied:
Current configuration for extension with alias "api_platform"
=============================================================
api_platform:
title: FooBar API
description: 'FooBar API, only for authenticated use'
version: 0.8.0
name_converter: null
path_segment_name_generator: api_platform.path_segment_name_generator.underscore
eager_loading:
enabled: true
fetch_partial: false
max_joins: 30
force_eager: true
And yet the results will be something like:
{
"@context": "/api/contexts/BoostLeadContactAction",
"@id": "/api/boost_lead_contact_actions/9",
"@type": "BoostLeadContactAction",
"id": 9,
"boostLead": "/api/boost_leads/30",
"channel": "/api/contact_channels/1",
"direction": "outgoing",
"successful": true,
"type": "/api/lead_contact_attempt_reasons/1",
"notes": "2",
"createdAt": "2019-05-16T10:27:33+00:00",
"updatedAt": "2019-05-16T10:27:33+00:00"
}
"boostLead", "channel" and "type" should be actual entities, eagerly loaded, but only IRIs are returned instead. I confirmed that the SQL query performed does not include any kind of join
.
Funnily enough, it seems to be the opposite trouble than this other user is having. I wish I had their problems.
What could be preventing these relationships to be loaded eagerly? The relationships work otherwise (I can do other queries with Doctrine, or create custom serialization groups and the related properties will be included).
Entity Framework Core allows you to use the navigation properties in your model to load related entities. There are three common O/RM patterns used to load related data. Eager loading means that the related data is loaded from the database as part of the initial query.
It is also possible to eagerly load multiple levels of related entities. The queries below show examples of how to do this for both collection and reference navigation properties. It is not currently possible to filter which related entities are loaded.
Loading of related entities can still be achieved using eager loading (see Eagerly Loading above) or the Load method (see Explicitly Loading below). Even with lazy loading disabled it is still possible to lazily load related entities, but it must be done with an explicit call. To do so you use the Load method on the related entity’s entry.
The techniques shown in this topic apply equally to models created with Code First and the EF Designer. Eager loading is the process whereby a query for one type of entity also loads related entities as part of the query. Eager loading is achieved by use of the Include method.
By default dereferenceable IRIs are used to display the related associations.
Looking at the executed statements you should not see an explicit JOIN
query but rather additional SELECT
statements for the related associations.
If you are wanting the JSON representation of the associated object.
You need to specify the Serialization
@Groups
for the desired properties in the related association.
This will cause the SELECT
statement to add in a JOIN
to retrieve the related data to be serialized.
For more details see https://api-platform.com/docs/core/serialization/#embedding-relations
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ApiResource(normalizationContext={ "groups": {"boost"} })
* @ORM\Entity()
* @ORM\Table(name="BoostLeadContactActions")
*/
class BoostLeadContactAction {
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
* @Groups({"boost"})
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\BoostLead", inversedBy="contacts")
* @ORM\JoinColumn(nullable=false)
* @Groups({"boost"})
*/
private $boostLead;
/**
* @ORM\ManyToOne(targetEntity="App\Entity\ContactChannel", fetch="EAGER")
* @ORM\JoinColumn(nullable=false, referencedColumnName="Id")
* @Groups({"boost"})
*/
private $channel;
// getters/setters/extra properties removed for brevity
}
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* @ApiResource()
* @ORM\Table(name="ContactChannels")
* @ORM\Entity
*/
class ContactChannel {
/**
* @var int
* @ORM\Column(name="Id", type="smallint", nullable=false, options={"unsigned"=true})
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
* @Groups({"boost"})
*/
private $id;
/**
* @var string
* @ORM\Column(name="Name", type="string", length=64, nullable=false)
* @Groups({"boost"})
*/
private $name = '';
}
Which should result in the normalized values being retrieved
{
"@context": "/api/contexts/BoostLeadContactAction",
"@id": "/api/boost_lead_contact_actions/9",
"@type": "BoostLeadContactAction",
"id": 9,
"boostLead": "/api/boost_leads/30",
"channel": {
"@id": "/api/contact_channels/1",
"@type": "ContactChannel",
"id": "1",
"name": "Test"
},
"direction": "outgoing",
"successful": true,
"type": "/api/lead_contact_attempt_reasons/1",
"notes": "2",
"createdAt": "2019-05-16T10:27:33+00:00",
"updatedAt": "2019-05-16T10:27:33+00:00"
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With