【问题标题】:Load specific columns from eager loaded laravel collection从预先加载的 laravel 集合中加载特定的列
【发布时间】:2021-09-28 15:00:42
【问题描述】:

考虑书籍作者的关系。每本书 belongsTo 一位作者和 1 个作者 hasMany 本书。 Books 表具有(Id、Title、Version 等)等字段,作者具有(Id、Name、Address 等)等字段。注意 DB 列不遵循默认的 laravel 命名约定。

现在;我想从两个表中获取 一些字段。更具体一点,我想要 title、version、name 和 address 字段。 这是所需的输出:

    {
        Title: "Oxford Maths",
        Version: "2.5",
        author: "John Doe",
    }

第一次试用:

return Book::with('author')->get()->map(function ($book) {
    return collect($book)->only(
        [
            'Title',
            'Version',
            "author"
        ]);
});

第一次试验输出:

    {
        Title: "Oxford Maths",
        Version: "2.5",
        author: {
            Id: 1,
            Name: "John Doe",
            Address: "Tanzania",
            deleted_at: null
        }
    }

第二次试用:试过这个;

return Book::with([
        'author' => function($query){
            $query->select('Name','Id');
        }
    ])->get()->map(function ($data) {
    return collect($data)->only(
        [
            'Title',
            'Version',
            "author"
        ]);
});

还有这个;

return Book::with('authority:name,Id')->get()->map(function ($data) {
    return collect($data)->only(
        [
            'Title',
            'Version',
            "author"
        ]);
});

第二次试验输出:

    {
        Title: "Oxford Maths",
        Version: "2.5",
        author: {
            Name: "John Doe",
            Id: 1
        }
    }

第三次试用:

return Book::with([
        'author' => function($query){
            $query->select('Name'); // *Removed foreignKey*
        }
    ])->get()->map(function ($data) {
    return collect($data)->only(
        [
            'Title',
            'Version',
            "author"
        ]);
});

第三次试验输出:

    {
        Title: "Oxford Maths",
        Version: "2.5",
        author: null
    }

我可以做些什么来获得所需的输出?

【问题讨论】:

  • Book::with('author : title, version, name , address, author_id')->get();这有帮助吗?
  • @psa 试过但没有用。
  • Book 表中是否有 author_id 字段?
  • 是的,有一个,关系是一对多的。

标签: php laravel eloquent laravel-8 laravel-collection


【解决方案1】:

当然,您可以事后在集合上执行此操作,但直接在查询上执行此操作更有意义。你试图在你的第三次试验中反映这一点。但是,在我看来,这是 Laravel behaves a bit odd。为了进行预加载,您需要外键。尽管这是合乎逻辑的,但人类还是会忘记这一点。但是如果你忘记包含外键,Laravel 不会告诉你这一点,而是返回 null。因此,请确保所有必要的主键和外键始终包含在您的关系中。

return Book::with([
    'author' => function($query){
        $query->select('id', 'Name'); // *Removed foreignKey*
    }
])->get([ 'title' , 'version' ]);

有一件事,当一个关系应该主要只返回它的一个值时,我喜欢做:

class Author extends Model
{
    public function __toString()
    {
        return $this->Name;
    }
}

要以这种方式自动序列化,您可以将__toString() 更改为jsonSerialize()

class Author extends Model implements JsonSerializable
{
    public function jsonSerialize()
    {
        return $this->Name;
    }
}

【讨论】:

  • 我试过这个。然而,我得到了第三次试验输出。如何在此处添加 author_name? ->get([ 'title' , 'version' ]);
  • author_name 是列还是访问器,还是还没有?
  • 我的意思是希望输出中的作者姓名。 author_name 不是列,而 name 是 authors 表中的列。
  • 由于某些原因,在使用这种方法时会得到相同的结果。我决定采用延迟加载方法而不是急切加载。看我的回答。
【解决方案2】:

也许对你有帮助

在模型中

protected $appends = [
    'author_name', 'address'
];

protected $fillable = [
    'title',
    'version',
];

public function author(){
    return $this->belongsTo(Author::class);
}

public function getAuthorNameAttribute(){
    return $this->author->name;
}

public function getAddressAttribute(){
    return $this->author->address;
}

在控制器中使用这个

return Book::query()->with('author')->first()->makeHidden(['author','author_id']);

输出:

{
  "id": 1,
  "title": "book",
  "version": "v1",
  "created_at": null,
  "updated_at": null,
  "author_name": "komeyl",
  "address": "Iran"
}

【讨论】:

  • 感谢您的贡献,但您能否将代码添加为文本而不是图像,以确保即使图像被删除,您的答案仍然有用?
【解决方案3】:

我最终使用了@komeyl-dev 建议的技巧。这是示例代码:

//Book.php
class Book extends Model
{
    protected $appends = ['author_name']; // appended so I can access it as json

    public function author(){
        return $this->belongsTo(Author::class,'author_id');
    }
    
    public function getAuthorNameAttribute(){
        return $this->author->name;
    }
}

//Testing in web.php (Note: No eager loading here)
Route::get('url', function () {
    return Book::get()->map(function ($data) {
        return collect($data)->only(
            [
                'Title',
                'Version',
                "author_name"
            ]);
    });
});

谢谢@shaedrich 和@komeyl-dev

【讨论】:

    猜你喜欢
    • 2015-05-08
    • 2017-11-20
    • 2016-08-09
    • 1970-01-01
    • 2020-12-05
    • 2013-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多