tamkeenlms/laravel-data-selector

在Laravel的Eloquent之上增加一层,以帮助从数据库中选择和检索数据。

v1.0 2018-01-04 11:48 UTC

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"