-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert Model to JsonApi object in a custom Controller. #36
Comments
Hi! Yes, extend the Note that you'll need to attach an adapter to so that all the parsing of the incoming request knows how to check whether a resource id is valid and can load the record that the id relates to. You need to extend the For an example of how this is done for Eloquent models, see this: I appreciate I really need to write some documentation... in the meantime feel free to ask any questions here. |
Hi lindyhopchris! thank you very much for your response! EDIT: I have found a way of doing what I want using neomerx encoder like this: ` (...) $myModel = new MyModel(); ` Does laravel-json-api have a provider or a way to resolve something like I resolved with neomerx in the example?? |
Hi! Yes, that's the solution for the moment - manually create an encoder and then encode the data yourself. I'm thinking that it would be really useful to have an approach (including a Blade directive) for outputting JSON API encoded data into a view. The use case you describe would be quite common in my opinion. I'm going to leave this issue open as it's effectively a feature request! |
Thank you very much! It'll be great to have something like that in a future. Another thing i've been trying to do and could be related to this in some way is to find a way to throw an exception or instance an error using the laravel-json-api errors. Reference: https://github.com/cloudcreativity/laravel-json-api/blob/master/config/json-api-errors.php#L67 If you consider I can open another issue for this case :) |
If you're using the So you can do the following on your controller:
|
Just a +1 vote for this, I came here looking for a way to "consume" the API within the Laravel code base. In my case, my web app is consuming its own JSON API for AJAX actions, and also I am using Laravel Echo to broadcast events. My app will have multiple people collaborating in the same space on separate devices, so other users need model events (create/edit/delete) broadcast to them. It would be swell for the broadcast JSON structure to follow the same format as the API does, because then I can use the same client-side code to process both cases (API response content and event broadcast content). Using your API package to do this ensures that in both cases the JSON structure would be identical even through design changes, which will make developmental do-it-by-myself testing more reliable. |
Broadcast is definitely an aim. We're using broadcast at the moment but having to do it manually as follows: /**
* Get the data to broadcast.
*
* @return array
*/
public function broadcastWith()
{
/** @var CloudCreativity\JsonApi\Encoder\Encoder $encoder */
$encoder = Encoder::instance([
Post::class => Posts\Schema::class,
Comment::class => Comments\Schema::class,
]);
return $encoder->serializeData($this->post);
} Obviously what would be nicer is to have a more fluent way to do this, and get the encoder already loaded with the schema definitions that you set in your config. If you are using the above as a temporary solution then remember to include in the array all the classes/schemas that could appear in the "tree" of resources that you are encoding. |
That is really helpful, thanks! |
Hello again. I would like the Broadcast response to include the attributes within some of the relationships. For the life of me I cannot figure out how to make the attributes include the attributes on relationships. Any tips? I have tried a bunch of stuff but here is how I have it set up at the moment. public function broadcastWith()
{
Log::info('Device event:', ['class' => self::class, 'data' => $this->device]);
$this->device->load('building', 'type');
return $encoder = Encoder::instance([
Device::class => Devices\Schema::class,
Building::class => Buildings\Schema::class,
DeviceType::class => DeviceTypes\Schema::class
])->serializeData($this->device);
} In its schema: public function getRelationships($resource, $isPrimary, array $includeRelationships)
{
if (!$resource instanceof Device) {
throw new RuntimeException('Expecting a Device model.');
}
return [
'building' => [
self::SHOW_SELF => true,
self::SHOW_RELATED => true,
self::DATA => $resource->building,
],
'device-type' => [
self::SHOW_SELF => true,
self::SHOW_RELATED => true,
self::DATA => isset($includeRelationships['device-type']) ?
$resource->type : $this->createBelongsToIdentity($resource, 'type'),
]
];
} whether or not I include "self::SHOW_DATA => true" above doesn't seem to affect the response when encoded in this way. High-level this works, except the response is the top-level attributes on the Device object, and the attributes for Building and DeviceType are missing. The log event is there just to prove that the Building model has the relationships loaded, even though I suspect the real intended method is to tell it to include the relationship by some other means. [2017-04-22 05:18:20] local.INFO: Device event: {"class":"App\\Events\\Broadcasts\\Device\\DeviceEvent","data":"[object] (App\\Models\\Building\\Device: {\"id\":49,\"building_id\":2,\"device_type_id\":1,\"designation\":\"31\",\"slug\":\"31\",\"group\":\"Test\",\"licence\":null,\"created_at\":\"2017-04-22 05:18:19\",\"updated_at\":\"2017-04-22 05:18:19\",\"building\":{\"id\":2,\"name\":\"Fake Building\"}})"} I am aware that the second parameter in serializeData() is a EncodingParametersInterface object but I cannot find documentation on this nor can I tell how this is meant to be used by reading through the code. |
Hello! You'll need to include the related resources. So you'll need to pass the encoder a set of encoding parameters, which with the include parameters as The actual |
Awesome, thanks! For those who look this up later, the solution is as follows: return Encoder::instance([
Device::class => Devices\Schema::class,
Building::class => Buildings\Schema::class,
DeviceType::class => DeviceTypes\Schema::class
])->serializeData($this->device, new EncodingParameters(['building', 'device-type'])); |
Great, glad that helped. I'm planning on adding a trait that can be applied to make broadcasting a lot easier. Haven't done it yet because in all the apps we use the package on, we're not doing any broadcasting yet (for legacy reasons), but we're planning to start adding in broadcasting soon. |
Oh hello... so, concerning version 0.8 and its config file changes, can you suggest a way to generate a schema relationships array in a way which produces the "schemas.defaults" config array used in version 0.7? The context is that I made my own hacky "getFromApi" helper function for use in broadcasters and initial state generation, and it was reading the config file to get all of the schema mappings so that I didn't have to worry about feeding it the schema relationships in every space. It looked like this: /**
* @param Eloquent|Collection|array $models
* @param string|array $includes
* @return mixed
*/
public function apiEncoder($models, $includes = []) {
if (is_string($includes)) {
$includes = [$includes];
}
return Encoder::instance(Config::get('json-api.schemas.defaults'))->serializeData($models, new EncodingParameters($includes));
} As of 0.8 that doesn't work, here is a even hackier example of how one could fix it: /**
* @param Eloquent|Collection|array $models
* @param string|array $includes
* @return mixed
*/
public function apiEncoder($models, $includes = []) {
if (is_string($includes)) {
$includes = [$includes];
}
$schemas = [
Models\Address\Address::class => JsonApi\Addresses\Schema::class,
Models\Building\Building::class => JsonApi\Buildings\Schema::class,
Models\Address\City::class => JsonApi\Cities\Schema::class,
Models\Client\ClientCompany::class => JsonApi\ClientCompanies\Schema::class,
Models\Contractor\ContractorCompany::class => JsonApi\ContractorCompanies\Schema::class,
Models\Address\Country::class => JsonApi\Countries\Schema::class,
Models\Address\CountryDivision::class => JsonApi\CountryDivisions\Schema::class,
Models\Building\Device::class => JsonApi\Devices\Schema::class,
Models\Building\DeviceType::class => JsonApi\DeviceTypes\Schema::class,
Models\Office\Office::class => JsonApi\Offices\Schema::class,
Models\Project\ProjectStatus::class => JsonApi\ProjectStatuses\Schema::class,
Models\Project\ProjectType::class => JsonApi\ProjectTypes\Schema::class,
Models\Project\Project::class => JsonApi\Projects\Schema::class,
Models\Task\Task::class => JsonApi\Tasks\Schema::class,
Models\Task\TaskType::class => JsonApi\TaskTypes\Schema::class,
];
return Encoder::instance($schemas)->serializeData($models, new EncodingParameters($includes));
} In the short-term, I am going to basically include this 'schemas' list in the config file, which is off-spec per your current designs. I figure that there might be a "getSchemas()" method somewhere but I cannot find it. If I use the JsonApi Facade, ie |
Hi! Yes, had forgotten you were doing that. Here's how to get the array: $repository = app(CloudCreativity\LaravelJsonApi\Api\Repository::class);
$schemasArray = $repository->retrieveApi('api-name')->getResources()->getSchemas(); It's a bit convoluted at the moment but I will be making it simpler in the near future (it's on the 1.0 to-do list). |
Awesome, thanks! For now, convoluted is fine, I understand that you intend to improve on the handling of this aspect so for now I am just using a helper and figure I can sub in your official solution whenever it is ready. |
Great! That code snippet actually won't change, it's just I'll hide it behind an "official" method to get an encoder by API name, which is effectively what you're currently doing that the mo. |
Sorry - slight typo in the snippet above, the namespace should be |
Unfortunately I am running in to some errors. First, I assume that given the config file "json-api-default.php", the intended API name is 'default'. Running the code as suggested (with the namespace fix), I get 'call to undefined method CloudCreativity\LaravelJsonApi\Schema\Container::getResources()' The IDE hints that I can call "getSchemas()" directly, as so: $repository = app(Repository::class);
$schemasArray = $repository->retrieveApi('default')->getSchemas(); This returns a container, not an array. One of the members is a property "-providerMapping", which includes the hyphen in the name, and I cannot figure out how to access it, however it contains the required array structure for the encoder. |
Really sorry, gave you the wrong method to invoke. Use |
So |
Thank you, this works as expected. |
For those who visit this in the future, here is the helper method I am using: use CloudCreativity\LaravelJsonApi\Api\Repository;
use CloudCreativity\JsonApi\Encoder\Encoder;
use Neomerx\JsonApi\Encoder\Parameters\EncodingParameters;
...
/**
* @param Eloquent|Collection|array $models
* @param string|array $includes
* @return mixed
*/
public function apiEncoder($models, $includes = []) {
if (is_string($includes)) {
$includes = [$includes];
}
return Encoder::instance(
app(Repository::class)->retrieveDefinition('default')->getResources()->getSchemas()
)->serializeData($models, new EncodingParameters($includes));
} |
An encoder can now be obtained for a named API using the JSON API service's `encoder` method. This contributes towards Issue #36
The following are on the BroadcastingCan now use the public function broadcastWith()
{
// $includes is optional, can be a string or array of strings
return $this->serializeData($this->user, $includes);
} This will use the Blade TemplatesIn a blade template, you can now do <script type="application/vnd.api+json">@encode($posts)</script> This will use the @jsonapi('v1', null, JSON_PRETTY_PRINT);
<script type="application/vnd.api+json">
@encode($posts)
</script> You only need to use |
Hello, I'd like to know if the package has any way to convert an object model to a json with the jsonapi structure in any controller (not necessarily an Eloquent jsonapi controller).
Thank you!
The text was updated successfully, but these errors were encountered: