laravel-json-api / laravel

JSON:API for Laravel applications
MIT License
523 stars 43 forks source link

Relationship not showing up #242

Closed genyded closed 1 year ago

genyded commented 1 year ago

OK, we're probably missing something simple here, but cannot seem to figure out what. We have two models User & Profile with a one-to-one relationship. The relationships work fine outside of JSONAPI (just standard Laravel). Our schemas are set up for both as follows:

<?php

namespace App\JsonApi\V1\Users;

use App\Models\User;
use LaravelJsonApi\Eloquent\Contracts\Paginator;
use LaravelJsonApi\Eloquent\Fields\DateTime;
use LaravelJsonApi\Eloquent\Fields\ID;
use LaravelJsonApi\Eloquent\Fields\Str;
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;
use LaravelJsonApi\Eloquent\Fields\Relations\HasOne;

class UserSchema extends Schema
{

    /**
     * The model the schema corresponds to.
     *
     * @var string
     */
    public static string $model = User::class;

    /**
     * Get the resource fields.
     *
     * @return array
     */
    public function fields(): array
    {
        return [
            ID::make(),
            Str::make('firstName', 'first_name'),
            Str::make('lastName', 'last_name'),
            Str::make('email', 'email'),
            Str::make('password')->hidden()->hidden()->deserializeUsing(
                static fn($value) => bcrypt($value)
            ),
            Str::make('password_confirmation')->hidden(),
            Str::make('verification_token')->hidden(),
            Str::make('verified')->hidden(),
            Str::make('admin')->hidden(),
            DateTime::make('createdAt')->sortable()->readOnly(),
            DateTime::make('updatedAt')->sortable()->readOnly(),
            HasOne::make('profile')->readOnly()
        ];
    }

    /**
     * Get the resource filters.
     *
     * @return array
     */
    public function filters(): array
    {
        return [
            WhereIdIn::make($this),
        ];
    }

    /**
     * Get the resource paginator.
     *
     * @return Paginator|null
     */
    public function pagination(): ?Paginator
    {
        return PagePagination::make();
    }

}
<?php

namespace App\JsonApi\V1\Profiles;

use App\Models\Profile;
use LaravelJsonApi\Eloquent\Contracts\Paginator;
use LaravelJsonApi\Eloquent\Fields\DateTime;
use LaravelJsonApi\Eloquent\Fields\ID;
use LaravelJsonApi\Eloquent\Fields\Relations\BelongsTo;
use LaravelJsonApi\Eloquent\Fields\Str;
use LaravelJsonApi\Eloquent\Filters\WhereIdIn;
use LaravelJsonApi\Eloquent\Pagination\PagePagination;
use LaravelJsonApi\Eloquent\Schema;

class ProfileSchema extends Schema
{

    /**
     * The model the schema corresponds to.
     *
     * @var string
     */
    public static string $model = Profile::class;

    /**
     * Get the resource fields.
     *
     * @return array
     */
    public function fields(): array
    {
        return [
            ID::make(),
            Str::make('avatar'),
            Str::make('bio'),
            DateTime::make('createdAt')->sortable()->readOnly(),
            DateTime::make('updatedAt')->sortable()->readOnly(),
            BelongsTo::make('user')->readOnly(),
        ];
    }

    /**
     * Get the resource filters.
     *
     * @return array
     */
    public function filters(): array
    {
        return [
            WhereIdIn::make($this),
        ];
    }

    /**
     * Get the resource paginator.
     *
     * @return Paginator|null
     */
    public function pagination(): ?Paginator
    {
        return PagePagination::make();
    }

}

When we query a user though, there is no reference at all to the profile:

{
  "jsonapi": {
    "version": "1.0"
  },
  "links": {
    "self": "http://localhost/api/users/1"
  },
  "data": {
  "type": "users",
   "id": "1",
   "attributes": {
     "firstName": "User",
     "lastName": "One",
      "email": "user1@email.com"
    },
    "links": {
      "self": "http://localhost/api/users/1"
    }
  }
}

There are no errors, and from the JSONAPI tutorial, we think we have all the pieces in place for this to work. So, what are we missing here?

X-Coder264 commented 1 year ago

By default you need to explicitly require the profile relationship in order for it to be included.

http://localhost/api/users/1?include=profile

https://jsonapi.org/format/#fetching-includes

genyded commented 1 year ago

Actually, that is for inclusion of the related data, not the links. Anyway, we figured it out. We had to add the relationships in the JSONAPI Resource files, but it seems the tutorial skips over that part. Closing since resolved.

<?php

namespace App\JsonApi\V1\Users;

use Illuminate\Http\Request;
use LaravelJsonApi\Core\Resources\JsonApiResource;

class UserResource extends JsonApiResource
{

    /**
     * Get the resource's attributes.
     *
     * @param Request|null $request
     * @return iterable
     */
    public function attributes($request): iterable
    {
        return [
            'firstName' => $this->first_name,
            'lastName' => $this->last_name,
            'email' => $this->email,
            $this->mergeWhen(auth('api')->user() && auth('api')->user()->isAdmin(), [
                'admin' => $this->admin,
                'createdAt' => $this->created_at,
                'updatedAt' => $this->updated_at

            ]),

            //'verificationToken' => $this->when($this->isAdmin() || (auth('api')->user() && auth('api')->user()->isAdmin()), $this->admin),
            //'createdAt' => $this->when(auth('api')->user() && auth('api')->user()->isAdmin(), $this->created_at),
            //'updatedAt' => $this->when(auth('api')->user() && auth('api')->user()->isAdmin(), $this->updated_at)
        ];
    }

    /**
     * Get the resource's relationships.
     *
     * @param Request|null $request
     * @return iterable
     */
    public function relationships($request): iterable
    {
        return [
            $this->relation('profile'),
        ];
    }

}