从预先加载的 laravel 集合中加载特定的列

Posted

技术标签:

【中文标题】从预先加载的 laravel 集合中加载特定的列【英文标题】:Load specific columns from eager loaded laravel collection 【发布时间】: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 字段? 是的,有一个,关系是一对多的。 laravel.com/docs/8.x/… 【参考方案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

【讨论】:

以上是关于从预先加载的 laravel 集合中加载特定的列的主要内容,如果未能解决你的问题,请参考以下文章

我想从我的 CSV 文件中加载特定的列数据(MySQL 8.0)

有啥方法可以避免在 Laravel 5.7 Eloquent 中加载父级时加载这些 $with 模型?

Laravel Eager Loading - 仅加载特定列

如何列出在特定类加载器中加载的所有类

Flask框架重点知识总结回顾

如何在uitableview中加载数据而不每次都重新加载