tamkeenlms / laravel-data-selector
在Laravel的Eloquent之上增加一层,以帮助从数据库中选择和检索数据。
Requires
- php: >=5.4
- spatie/macroable: ^1.0
This package is not auto-updated.
Last update: 2024-09-25 08:36:57 UTC
README
这是一个相对简单的额外层,它建立在Laravel惊人的Eloquent之上。其目标是帮助您在代码库中使数据库的数据选择/检索更加简洁和标准化。
为什么以及它是如何工作的
好吧,使用Laravel时,我们通常使用我们创建的模型来选择数据(如Client::all()),Eloquent当然通过提供如where(...)、limit(...)等方法,使自定义此简单查询非常直接。这当然很棒,但当您发现自己重复使用大量的这些Where、OrderBy、Limit等时,问题就出现了。这些方法实际上代表了整个应用程序背后的大部分逻辑,因为您使用它们来控制最终将显示给用户的输出。由于我们总是在不同的地方不断更改这些条件,所以我们总是得到不同的查询,而这些查询本应该做相同的事情!
当然,您可以使用作用域来解决这个问题,这足够了,但我正在尝试提出一种不同的方法,一种您可以将其视为更干净、更优雅的方法,以避免污染您的模型。您将只需为每个模型创建一个API,一个“选择器”,您可以在任何地方调用它并请求特定的查询。让我们以一个例子来说明;您有一个Client模型,它代表您的“客户”数据库表
class Client extends Model{ public $table = 'clients'; protected $guarded = []; public function orders(){ return $this->hasMany(Order::class); } }
以后您将发现自己添加更多这样的作用域
public function scopeActiveOnly($query){ return $query->where('active', true); } public function scopeRegisteredThisMonth($query){ return $query->whereMonth('created_at', \Carbon::now()); }
使用这个库,您可以将这些作用域从模型中分离到更干净的接口中,在那里您可以更好地关注它们,将它们分组成有意义的API调用,并更好地服务于您的应用程序逻辑!
如何使用它
安装
简单地将它通过composer拉入您的项目
composer require tamkeenlms/laravel-data-selector
现在,对于您拥有的每个模型,您将创建一个选择器类,一个将扩展DataSelector\Selector的类,然后您将能够调用这个选择器并请求x或y。我建议您在/app下创建一个用于选择器的新的目录。
<?php namespace app\selectors; class Clients extends DataSelector\Selector{ public function __contructor(){ parent::__construct(Client::class); } } $clients = new Clients; $clients->get(); // All clients (Collection)
在这里,我们为Client模型创建了一个选择器类。在这个类的构造函数中,我们需要调用父类(DataSelector\Selector)的构造函数并传递模型。除了模型之外,您还可以传递每次调用此选择器时应选择默认列。此外,您可以从每个选择器的实例接收这些列。此外,使用每个实例,您可以要求它包括已删除(软删除)的项目。
<?php namespace app\selectors; class Clients extends DataSelector\Selector{ public function __contructor(array $columns = null, $withDeleted = false){ $defaultColumns = ['id', 'name', 'created_at']; // The default, in case the instance didn't provide a list parent::__construct(Client::class, $columns, $defaultColumns, $withDeleted); } } $clients = new Clients(['id', 'name']); // Will select only the id and the name for each Client (excluding the trashed ones) $allClients = new Clients(['*'], true); // Will select * from all clients, including the trashed
现在,在选择器实例内部,您可以定义帮助您表示与数据选择相关的逻辑部分的方法
public function activeAndNew(){ $this->where('active', true) ->where('created_at', '=>', Carbon::parse('2018-01-01')); }
然后您可以从这个选择器的任何实例中调用它。
$clients = (new Clients())->activeAndNew()->get();
选择器方法
select($olumns, $override)
您可以使用此方法设置最终查询中要选择的列。默认情况下,这将添加到之前在构造函数中设置的现有列列表中,但您可以通过在列列表旁边传递true来覆盖此列表。
$clients->select(['id', 'name']); $clients->select('id, name, LEFT(bio) AS `bio`'); // You can also provide a raw statement $clients->select(['*'], true); // Overrides the above
includeTrashed()
这将包括最终的查询结果中的已删除(软删除)的记录。这当然取决于Laravel的SoftDeletes特性,如果在使用此方法的模型中未调用该特性将引发错误。您可以在这里了解更多有关Laravel中软删除的信息。
onlyTrashed()
这将仅返回已删除(软删除)的记录。
where(...$args)
您可以使用此方法的方式与在模型上使用的方式相同。
$clients->where('id', 1); $clients->where('id', '=', 1); $clients->where('id', '!=', 1); $clients->where('name', 'LIKE', '%John%');
whereIn($column, $values)
这创建了一个WHERE IN语句。
$clients->whereIn('id', [1, 2, 3]);
orderBy($column, $asc = true)
向查询中添加一个ORDER BY语句。
$clients->orderBy('created_at'); // Create first at first $clients->orderBy('created_at', false); // Created last at first
latestFirst()
它将结果按“created_at”值最新优先排序。
$clients->lastestFirst();
oldestFirst()
最旧的行(基于“created_at”的值)将位于顶部。
lastModifiedFirst()
lastModifiedLast()
cancel()
这将取消整个查询并返回一个空集合。
$clients = new Clients; if($request->ids){ $clients->whereIn('id', $request->ids); }else{ $clients->cancel(); } return ['clients' => $clients];
get()
返回结果(作为集合)
$clients = (new Clients(['id', 'name']))->get();
getCount()
仅返回结果的计数。
isEmpty()
返回是否为您查询找到记录。
isNotEmpty()
getSQL()
返回您创建的查询的SQL代码。
$clients = new Clients(['id', 'name']); $clients->activeOnly(); $clients->getSQL(); // select id, name from clients where active = 1
分页
Laravel使分页变得非常简单直观。在这里也是如此,只需使用paginate($itemsPerPage, $queryString)。其中$itemsPerPage是每页的项目数量,而$queryString是可选参数,用于附加到分页URL的任何URL查询字符串值。
$clients = new Clients(); $clients->paginate(5, ['format' => 'csv']);
现在,而不是返回一个集合,get()将返回一个Illuminate\Contracts\Pagination\LengthAwarePaginator实例。
宏
此库使用spatie/macroable,以允许您向库添加自己的方法,并使用任何选择器全局使用它们。
DataSelector\Selector::macro('whereActive', function(){ return $this->where('active', true); }); DataSelector\Selector::macro('whereName', function($name){ return $this->where('name', 'LIKE', '%' . $name . '%'); }); $clients = new Clients; $clients->whereActive()->whereName('John');
您还可以使用DataSelector\Selector::defineWhere()创建上面的相同代码。
DataSelector\Selector::defineWhere('active', function(){ return $this->where('active', true); }); $clients = new Clients; $clients->whereActive(); // the "where" is added automatically $users = new Users; $users->whereActive();
预加载
Eloquent提供的最有用功能之一是预加载,我本人使用它来尽可能减少查询次数。您可以在这里了解更多有关它的信息。DataSelector为此功能提供了一个快捷方式。例如
$clients = new Clients; $clients->eagerLoading() ->add('orders') ->add('favourites');
eagerLoading()->add($relation, array $columns = null, $where = null, $withTrashed = false)
这将在数据检索后添加一个新的预加载调用。这里的$relation代表模型类中定义的关联(方法)名称。而$columns将允许您指定应为此其他模型返回哪些列。$where允许您为查询添加WHERE语句,最后$withTrashed将允许您将已删除(软删除)的行包含在最终结果中。您也可以使用with(...)代替eagerLoading()->add(...)。
$clients = new Clients; $clients->with('orders'); $clients->with('orders', ['id', 'date'], 'MONTH(date) = 6 AND YEAR(date) = 2017'); $clients->with('orders', ['id', 'date'], ['date', '>=', Carbon::yesterday()], true);
当然,每个关联的值将按与Laravel提供相同的方式添加到最终结果下的父对象中。
结果格式化
DataSelector还将帮助您格式化最终输出。您可以定义选择器的格式化器或定义一个全局格式化器,您可以用任何选择器调用它。例如
$clients = new Clients; $clients->formatters()->add('name', function($name){ // Where "name" is the name of the column targeted for formatting return 'Mr. ' . $name });
在上面的示例中,最终集合中的每个结果都将有一个包含原始值的name和一个包含“Mr. [name]”值的name_formatted。您也可以使用format()作为快捷方式。
$clients->format('name', ...)
您还可以设置全局格式化器,如下所示
DataSelector\Formatter::setGlobalFormatter('YMD', function($date){ return $date->format('Y-m-d'); }); $clients = new Clients; $clients->format('created_at', 'YMD'); // Will return "created_at_formatted" with the time formatted as Y-m-d
格式化也适用于预加载的值,例如
$clients = new Clients; $clients->with('orders', ['id', 'date']); $clients->format('orders.date', 'YMD'); // Where "date" is a property of the eager-loaded values of "orders"