首页 > 解决方案 > Laravel 与资源的关系-> 试图获取非对象的属性“id”

问题描述

我试图从高处和低处寻找解决方案,但失败了。所以这里是:

我正在为网站制作照片库模块。我有一个名为“GalleryName”的模型和另一个名为“GalleryPhoto”的模型,并在它们之间建立了 hasMany 关系。

我正在使用 Vue JS 和我用来显示照片的包要求我使用单词“title”作为照片名称的键,并使用“src”作为我存储的文件的完整路径的键。我决定使用资源文件来更改键和值,而不是更改我的数据库和其他逻辑。

首先,这是我的迁移:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateGalleryNamesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('gallery_names', function (Blueprint $table) {
            $table->id();
            $table->string('gallery_name');
            $table->string('gallery_cover_photo');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('gallery_names');
    }
}
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateGalleryPhotosTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('gallery_photos', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('gallery_name_id');
            $table->string('photo_title');
            $table->string('photo_image');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('gallery_photos');
    }
}

这是我的模型:

// Gallery Name
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class GalleryName extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function gallery_photos(){
        return $this->hasMany(GalleryPhoto::class);
    }
}
// Gallery Photo
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class GalleryPhoto extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function gallery_name(){
        return $this->belongsTo(GalleryName::class);
    }  
}

这是我的资源文件:

// Gallery Name Resource
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

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

        return [
            'id' => $this->id,
            'gallery_name' => $this->gallery_name,
            'gallery_cover_photo' => $this->gallery_cover_photo,
            'created_at' => $this->created_at,
            'gallery_photos' => GalleryPhotoResource::collection($this->gallery_photos)
        ];   

    }
}

我正在使用这一行,'gallery_photos' => GalleryPhotoResource::collection($this->gallery_photos),因为当您需要在关系中使用资源时,文档会告诉您这样做。

// Gallery Photo Resource
<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

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

        return [
            'id' => $this->id,
            'gallery_name_id' => $this->gallery_name_id,
            'title' => $this->photo_title,
            'src' => '/storage/photo_gallery_images/'.$this->photo_image,
            'created_at' => $this->created_at,            
        ];       
    }
}

我正在使用这一行,'src' => '/storage/photo_gallery_images/'.$this->photo_image,因为我只将文件名存储在数据库中,现在我需要完整的限定路径,而不仅仅是我的包中的文件名要求。

我在控制器中声明了一个 show 方法,如下所示:

// Gallery Name Controller
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\GalleryName;
use App\Http\Resources\GalleryNameResource;

class GalleryNameController extends Controller
{

public function show($id)
{
        $gallery_name = new GalleryNameResource(GalleryName::where('id', $id)->select('id', 'gallery_name','gallery_cover_photo' , 'created_at')->with(['gallery_photos' => function($query) {
            return $query->select(['id', 'gallery_name_id', 'photo_title', 'photo_image', 'created_at']);
        }])->first());

        if($gallery_name){
            return response()->json($gallery_name, 200);
        }

        return response()->json('Gallery Not Found !!', 404);
    }
}

如您所见,这是使用关系,但也使用了我之前声明的资源文件。

现在,当我使用 Postman 并提供存在的 id(作为路由参数)时,它会显示正确的 JSON 响应。例如

{
    "id": 1,
    "gallery_name": "Some Gallery 1",
    "gallery_cover_photo": "pexels-lena-goncharova-8692688-400x300_1628567178.jpg",
    "created_at": "2021-08-10T03:46:18.000000Z",
    "gallery_photos": [
        {
            "id": 1,
            "gallery_name_id": 1,
            "title": "Photo 1",
            "src": "/storage/photo_gallery_images/pexels-lena-goncharova-8692688-900x800_1628567237.jpg",
            "created_at": "2021-08-10T03:47:17.000000Z"
        },
        {
            "id": 2,
            "gallery_name_id": 1,
            "title": "Photo 2",
            "src": "/storage/photo_gallery_images/pexels-lena-goncharova-8692688-400x300_1628567251.jpg",
            "created_at": "2021-08-10T03:47:31.000000Z"
        }
    ]
}

但是,当我提供数据库中不存在的 id 时,它会给我一个“尝试获取非对象的属性 'id'”错误。我找到了一种解决方法,但以一种 hacky 的方式。无论如何,我想知道这个错误的性质或为什么会发生这种情况,因为我找不到我做错了什么。

标签: laraveleloquent

解决方案


GalleryNameResource是这里的问题。

让我们重新格式化您的代码,使其更易于阅读:

public function show($id)
{

        $gallery_instance = GalleryName::where('id', $id)->select('id', 'gallery_name','gallery_cover_photo' , 'created_at')->with(['gallery_photos' => function($query) {
            return $query->select(['id', 'gallery_name_id', 'photo_title', 'photo_image', 'created_at']);
        }])->first();

        // $gallery_name == null if id doesn't exist
        
        $gallery_name = new GalleryNameResource($gallery_instance);

        // again, if the id doesn't exist, you are passing null to the resource, 
        // it's like writing new GalleryNameResource(null);

        // Remember: $gallery_name, in your code, IS NOT a GalleryName instance. 
        // It is a GalleryNameResource instance. 
        // It'll never be null or false, so the next condition is always true.

        if($gallery_name){
            return response()->json($gallery_name, 200);
        }

        return response()->json('Gallery Not Found !!', 404);
    }
}

当您发送GalleryNameResource响应时,Laravel 会将其转换为 JSON。

这就是您收到错误的原因:

public function toArray($request)
    {
        // return parent::toArray($request);

        return [
            'id' => $this->id,

            // 'id' => $this->id, is very likely your error

           // Laravel will try to get the id of... null
           // $this->id translates to $this->resource->id
           // so it translates to $this->null->id

           // Trying to get property id.... Error.

            'gallery_name' => $this->gallery_name,
            'gallery_cover_photo' => $this->gallery_cover_photo,
            'created_at' => $this->created_at,
            'gallery_photos' => GalleryPhotoResource::collection($this->gallery_photos)


        ];   

    }

如何解决您的问题?

public function show($id)
{
        $gallery_name = GalleryName::where('id', $id)->select('id', 'gallery_name','gallery_cover_photo' , 'created_at')->with(['gallery_photos' => function($query) {
            return $query->select(['id', 'gallery_name_id', 'photo_title', 'photo_image', 'created_at']);
        }])->first();

        if(!$gallery_name){
           return response()->json('Gallery Not Found !!', 404);
        }

        return response()->json(new GalleryNameResource($gallery_name), 200);
    }
}

解决此问题的一种更简洁的方法是使用隐式路由绑定,请在文档中查看:https ://laravel.com/docs/8.x/routing#implicit-binding


推荐阅读