Open bobmulder opened 7 years ago
I'm doing the exact same thing.
In my application-scenario I would use different Transformers in my Controller. E.g., there would be a ProductTransformer_Moderator
and a ProductTransformer_User
.
Things could get a bit nasty if you would output different strcutures depending on Roles
and/or Permissions
, so it may be easier to just have different Transformers.. What do you think?
Furthermore, you would need to inject the currentUser
to each Transformer - which kind of binds
the Transformer to your User Model..
Have the same issue... Is there any beautiful solution?
@hegoku , I would suggest to use different Transformers
in this use-case. I am not a fan of putting to much logic into the Transformers
. Please note that you can extend some kind of BaseProjectTransformer
which holds your includes
(and the respective relationships) and implement the required transform(...)
method in your specific classes..
e.g, like this:
abstract class AbstractProjectTransformer extends Transformer {
$availableIncludes = ['your', 'custom', 'includes'];
includeYour(Project $project) {
// do some stuff here, like
return $this->collection($project->something(), new WhateverTransformer());
}
// other include methods here
}
class UserProjectTransformer extends AbstractProjectTransformer {
transform(Project $project) {
return [
// your custom structure for USER roles here
];
}
}
class ManagerProjectTransformer extends AbstractProjectTransformer {
transform(Project $project) {
return [
// your custom structure for MANAGER roles here
];
}
}
The proposed solution is way easier to extend (e.g., add new attributes) than constantly adding new if / else
statements.. Further, it helps to "decouple" the transformers from the currentUser
that called a specific endpoint. Transformers
are - as they should be - only classes that transform one class to another one - without having any additional dependencies (like the user that wants to transform the object).
Hope this helps.. all the best
@johannesschobel This solution is nice, thank you very much!
@johannesschobel , I used this approach and mixed in some advanced transformation with includes, which adds some nice extra flexibility to my APIs endpoints.
BUT.. I am have trouble making this work with collection, which contains multiple types of items (models) and therefor should delegate the transformation of each of the items to a different transformer.
To continue with the user example, when including a collection of users with different roles, how do I tell fractal to use a different transformer based on the user's role, and make sure nested includes are passed through?
I have been stuck on this problem for days, and can't find a solution. Thanks for anybody's help!
@yoannisj , what exactly do you mean by
collections, which contains multiple types of items
?
Do you mean something like this:
$data = [
$book1,
$author1,
$book2,
$car,
$user1,
$user2,
// ...
];
Are you actually mixing different resources in one response? if yes, i don't think that this is the desired behaviour or how you should implement it.. Usually you have one "root" resource (e.g., determined by the URL, /api/books
) and the other resources should be "embedded" (e.g., through the includesX
methods..
Hope this helps
@johannesschobel, I am building an API for a website which contains listing/index pages, including a list of entries (a.k.a. posts). The endpoint to that page /api/pages/some-listing-page?include=entries
returns something like so:
$data = [
"title" => "Some listing page",
"url" =>"https://www.example.com/some-listing-page",
"postDate" => "2019-05-08T12h30"
"intro" => "Lorem ipsum dolor…",
"entries" => [
$newsItem, $article, $article, $event, $newsItem, $article, /* ... */
],
/* ... */
];
The "root" transformer for that page loads the data for the "entries" key, through an includeEntries
method, which returns a fractal CollectionResource
. But, the data-structure of the entry items in that collection can differ quite a lot from one entry type to another, some having their own nested includes (for example an article entry could respond to "?include=entries.writers"
, and an event entry could respond to another"?include=entries.location"
).
The examples I have seen in the documentation, seem to suggest that I can only pass a single transformer class to the collection returned in the page transformer's includeEntries
method. Something like:
public function includeEntries( $page )
{
$entries = new Query('entries')->parentPage($page)->fetch();
return $this->collection($entries, new EntryTransformer());
}
But that would mean, that the EntryTransformer
needs to implement the data transformation logic for all the different entry types, and all the possible nested includes in one single transformer class, which gets messy very quickly.
Ideally, there would be a way to tell Fractal to:
a) use a different transformer class for each type of entry included in the $entries collection (i.e. NewsEntryTransformer
, ArticleEntryTransformer
, …)
b) pass the nested include queries from the endpoint url to all the entry transformers (in such way that "?include=entries.writers" would call
ArticleEntryTransformer::includeWriters()` for example)
Is that possible, and how can I achieve such behaviour? Maybe the answer is to rely on a common PolyEntryTransformer
class, which gets passed to the included collection of entries, and then instanciates a sub-transformer for each entry, while passing in the nested include scope? How would that look like?
Sorry, this starts to look more and more like a stack overlfow ticket, rathat than a github issue. I would be happy to re-iterate my question in a more appropriate channel.
Thanks a lot for the help!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 4 weeks if no further activity occurs. Thank you for your contributions.
While using Fractal, I want to base my output on the given scopes or roles from logged in users. My question is, can this be done inside the transformer? Of course, in my workflow it can be done, but I'm not sure if it follows the 'rules' of Fractal.
Example code would be:
Thanks in advance!