ساخت eloquent api resource در لاراول

203
ساخت eloquent api resource در لاراول

در ساخت api یکی از مسائل و مشکلات، نحوه نمایش اطلاعات است. به کد زیر دقت کنید:

public function show(Book $book)
{
    return response()->json([
        'data' => [
            'title' => $book->title,
            'description' => $book->description,
            'author' => $book->author->name
        ]
    ]);
}

دقت کنید آیا فیلدهای created_at و updated_at حذف شده اند ؟
حالا یک مثال دیگر را ببینید، برای زمانی که میخواهیم یک object را ویرایش کنیم:

public function update(Request $request, Book $book)
{
    $book = $book->update($reques->all());
    return response()->json([
        'data' => [
            'title' => $book->title,
            'description' => $book->description,
        ]
    ]);
}

اگر بخواهیم که فیلد created_at را نمایش ندهیم میتوانیم مدل را تغییر بدهیم به صوری که created_at نمایش داده نشود ولی درعوض تاریخ به سبک دیگری برای کاربر قابل نمایش باشد. برای این کار متغیری در مدل وجود دارد که با آن بعضی از فیلد ها را میتوانیم hidden کنیم.
و نیز یک فیلد جدید به خروجی اضافه کنیم که با استفاده از متغیر append این کار انجام میشود:

// Book Model
protected $hidden = ['created_at', 'updated_at'];


// add new method to format date
protected $appends = ['date_stored'];

public function getDateStored()
{
    return (string) $this->created_at->diffForHumans();
}

حالا فکر کنید روی خیلی از فیلدهای مدل نیاز دارید که تغییرات ایجاد کنید، و این قضیه باعث میشود که مدل شما بیش از حد شلوغ شود، و به مرور زمان شما را برای برای تغییرات محدود میکند.
زیرا که تغییراتی که روی مدل اعمال میکنید، روی همه خروجی های شما تاثییر میگذارد.
برای مثال، شاید شما بخواهید برای یک api تاریخ را نمایش بدهید، برای یک api دیگر نمایش ندهید، ولی با روش بالا این کار برای شما سخت خواهد شد و حتی ممکن است که نتوانید این کار را انجام دهید.

Fractal : 
قبل از لاراول 5.5 پکیجی به نام fractal مورد استفاده خیلی از برنامه نویسانی بود که میخواستند خروجی api خود را تغییرات دهند.
در واقع با این کار یه لایه transformer به پروژه اضافه میشود که وظیفه تغییر و یکپارچه کردن خروجی در api را به عهده دارد.

Laravel api resource :
این ویژگی ، از لاراول 5.5 به لاراول اضافه شد. که میتوانید خیلی ساده از آن استفاده کنید. درصورتی که پیاده سازی fractal ممکن بود کمی شما را در پیاده سازی اذیت کند !
برای توضیح  بیشتر api resource از مثال استفاده میکنم. برای ساخت از دستور زیر استفاده کنید

php artisan make:resource yourResourceName

برای مثال برای ساخت یک resource برای مدل Book دستور زیر را مینویسیم:

php artisan make:resource Book

این فایل در مسیر زیر ساخته میشود:

app/Http/Resource

که محتویات آن به صورت زیر است:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class Book extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request $request
     * @return array
     */
    public function toArray($request)
    {
        return parent::toArray($request);
    }

}

حالا شما میتوانید این فایل را به راحتی تغییر دهید و خروجی را به شکلی که خودتان نیاز دارید پیاده سازی کنید:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class Book extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request $request
     * @return array
     */
    public function toArray($request)
    {
        return [
           'id' => $this->id,
           'title'=>$this->title
           'created_at' => $this->created_at
        ]
        
    }

}

حالا برای استفاده یک api تعریف میکنیم:

# routes/api.php

[...]
use App\Http\Resources\Book as BookResource;
use App\Book;
[...]

Route::get('/books/{book}', function(Book $book) {
    return new BookResource($book);
});

Route::get('/books', function() {
    return new BookResource::collection(Book::all());
});

 

{
  data: {
    id: 1,
    title: "ّFirst Book",
    created_at: "2019-03-09 12:37:27"
  }
}



// second route
{
    "data": [
        {
            id: 1,
            title: "ّFirst Book",
            created_at: "2019-03-09 12:37:27"
        },
        {
            id: 2,
            title: "ّSeconf Book",
            created_at: "2019-03-09 12:40:40"
        },
}

اگر به خروجی json بالا دقت کنید میبینید که طوری که شما اطلاعات را لازم داشتید، به شما نمایش داده شده است.
حالا اگر شما بخواهید اطلاعات دیگری را هم به collection اضافه کنید راه دیگری هم وجود دارد که میتوانید با ساختن و اضافه کردن کلمه collection به یک resource جدید، یک فایل جداگانه برای collection داشته باشید.

php artisan make:resource BookCollection

حالا فایلی که ساخته شده را باز کنید. مثلا میخواهید وقتی که همه book ها را گرفتید، تعداد آنها هم نمایش دهید. تغییرات زیر را اعمال کنید:

class SongsCollection extends ResourceCollection
{
    public function toArray($request)
    {
        return [
            'data' => $this->collection,
            'meta' => ['book_count' => $this->collection->count()],
        ];
    }
}

حالا فایل route را هم تغییر دهید:

[...]
use App\Http\Resources\BookCollection;
[...]
Route::get('/books', function() {
    return new \BookCollection(Book::all());
});

میبینید که خروجی به صورت زیر خواهد بود:

{
  data: [{
      id: 1,
      title: "First book",
      created_at: "2019-03-09 12:37:27",
      updated_at: "2019-03-09 12:37:27"
    },
    {
      id: 2,
      title: "Second book",
      created_at: "2019-03-09 12:40:40",
      updated_at: "2019-03-09 12:40:40"
    }
  ],
  meta: {
    book_count: 2
  }
}

ویژگی های متعددی برای resource ها میتوانید داشته باشید. که در مقاله بعدی توضیح خواهم داد.

اگر سوالی داشتید از قسمت نظرات میتوانید بپرسید.




نظرات
ali reza
1397/12/27
مثلن به صورت زیر:

return [
'order_code' => $this->order_code,
'date' => $this->date,
'status' => $this->status,
'totalCost' => $this->totalCost,
'products'=>$this->products->name,
]

ولی این کد اشتباه هست، چون ما برای products، متغیر name نداریم...
به نظر شما چ جوری محصولاتم رو توسط resource نشون بدم؟
حمید تیموری
1397/12/27
وقتی جدول خرید رو دارید نمایش میدید به متد with توی resource میشه روابط رو load کرد، و چون شما چندتا محصول دارید، نمیتونید name رو مستقیما چاپ کنید. چونکه name روی کل collection تعریف نشده، و برای هر محصول بش دسترسی دارید.
این مطلب رو من در مقاله بعدی توضیح میدم
ali reza
1397/12/27
سلام خدمت شما
ی سوال داشتم
من ی resource دارم که فقط یک سطر از جدول خرید رو بر میگردونه... به صورت زیر:

return [
'order_code' => $this->order_code,
'date' => $this->date,
'status' => $this->status,
'totalCost' => $this->totalCost,
]


حالا میخام محصولات مربوط به این خرید رو نیز، برگردونه...
برای ثبت نظر ابتدا وارد سایت شوید