数据仓库
<h1>数据仓库</h1>
<p>数据仓库(<code>Repository</code>)是<code>Dcat Admin</code>中对数据增删改查操作接口的具体实现,通过<code>Repository</code>的介入可以让页面的构建不再关心数据读写功能的具体实现,开发者通过实现<code>Repository</code>接口即可对数据进行读写操作。</p>
<p>> {tip} 当然为了方便系统也保留了直接使用 <code>Model</code> 的功能,底层会自动把<code>Model</code>转化为数据仓库实例,毕竟大多数时候直接使用 <code>Model</code> 也能满足我们的需求。</p>
<p>数据表格<code>Grid</code>、数据表单<code>Form</code>、数据详情<code>Show</code>、<code>Tree</code> 等组件不再直接依赖于<code>Model</code>,而是依赖于提供更简单清晰的接口的数据仓库,下面是<code>Repository</code>的所有接口:</p>
<pre><code class="language-php">&lt;?php
namespace Dcat\Admin\Contracts;
use Dcat\Admin\Form;
use Dcat\Admin\Grid;
use Dcat\Admin\Show;
use Illuminate\Support\Collection;
interface Repository
{
/**
* 获取主键名称.
*
* @return string
*/
public function getKeyName();
/**
* 获取创建时间字段.
*
* @return string
*/
public function getCreatedAtColumn();
/**
* 获取更新时间字段.
*
* @return string
*/
public function getUpdatedAtColumn();
/**
* 是否使用软删除.
*
* @return bool
*/
public function isSoftDeletes();
/**
* 获取Grid表格数据.
*
* @param Grid\Model $model
*
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator|Collection|array
*/
public function get(Grid\Model $model);
/**
* 获取编辑页面数据.
*
* @param Form $form
*
* @return array|\Illuminate\Contracts\Support\Arrayable
*/
public function edit(Form $form);
/**
* 获取详情页面数据.
*
* @param Show $show
*
* @return array|\Illuminate\Contracts\Support\Arrayable
*/
public function detail(Show $show);
/**
* 新增记录.
*
* @param Form $form
*
* @return mixed
*/
public function store(Form $form);
/**
* 查询更新前的行数据.
*
* @param Form $form
*
* @return array|\Illuminate\Contracts\Support\Arrayable
*/
public function updating(Form $form);
/**
* 更新数据.
*
* @param Form $form
*
* @return bool
*/
public function update(Form $form);
/**
* 删除数据.
*
* @param Form $form
* @param array $deletingData
*
* @return mixed
*/
public function delete(Form $form, array $deletingData);
/**
* 查询删除前的行数据.
*
* @param Form $form
*
* @return array|\Illuminate\Contracts\Support\Arrayable
*/
public function deleting(Form $form);
}
</code></pre>
<p>如果你的数据是多层级结构,则还需要实现以下接口</p>
<pre><code class="language-php">&lt;?php
namespace Dcat\Admin\Contracts;
interface TreeRepository
{
/**
* 获取主键字段名称.
*
* @return string
*/
public function getPrimaryKeyColumn();
/**
* 获取父级ID字段名称.
*
* @return string
*/
public function getParentColumn();
/**
* 获取标题字段名称.
*
* @return string
*/
public function getTitleColumn();
/**
* 获取排序字段名称.
*
* @return string
*/
public function getOrderColumn();
/**
* 保存层级数据排序.
*
* @param array $tree
* @param int $parentId
*/
public function saveOrder($tree = [], $parentId = 0);
/**
* 设置数据查询回调.
*
* @param \Closure|null $query
*
* @return $this
*/
public function withQuery($queryCallback);
/**
* 获取层级数据.
*
* @return array
*/
public function toTree();
}</code></pre>
<h2>Repository接口</h2>
<h3>getKeyName</h3>
<p>此接口要求返回数据的主键字段名称,需要返回<code>string</code>类型值。</p>
<pre><code class="language-php"> public function getKeyName()
{
return 'id';
}</code></pre>
<h3>getCreatedAtColumn</h3>
<p>此接口要求返回数据的<code>created_at</code>字段名称,如果没有值可以返回空字符串或<code>null</code>值。</p>
<pre><code class="language-php"> // 如果没有值可以返回空字符串或`null`值
public function getCreatedAtColumn()
{
return 'created_at';
}</code></pre>
<h3>getUpdatedAtColumn</h3>
<p>此接口要求返回数据的<code>updated_at</code>字段名称,如果没有值可以返回空字符串或<code>null</code>值。</p>
<pre><code class="language-php"> // 如果没有值可以返回空字符串或`null`值
public function getCreatedAtColumn()
{
return 'updated_at';
}</code></pre>
<h3>isSoftDeletes</h3>
<p>此接口要求返回数据是否支持软删除,请返回一个<code>bool</code>类型的值。</p>
<pre><code class="language-php"> public function isSoftDeletes()
{
return true;
}</code></pre>
<p><a name="get"></a></p>
<h3>get</h3>
<p>此接口要求返回数据表格<code>Grid</code>的数据,用于数据表格展示,要求返回一个<code>array</code>、<code>Illuminate\Support\Collection</code>或<code>LengthAwarePaginator</code>类型值。</p>
<h4>分页</h4>
<p>当数据需要分页时要求返回一个<code>\Illuminate\Contracts\Pagination\LengthAwarePaginator</code>类型值:</p>
<pre><code class="language-php"> public function get(Grid\Model $model)
{
// 获取当前页数
$currentPage = $model-&gt;getCurrentPage();
// 获取每页显示行数
$perPage = $model-&gt;getPerPage();
// 获取筛选参数
$city = $model-&gt;filter()-&gt;input(Grid\Filter\Scope::QUERY_NAME, '广州');
$start = ($currentPage - 1) * $perPage;
$client = new \GuzzleHttp\Client();
$response = $client-&gt;get(&quot;{$this-&gt;api}?{$this-&gt;apiKey}&amp;city=$city&amp;start=$start&amp;count=$perPage&quot;);
$data = json_decode((string)$response-&gt;getBody(), true);
return $model-&gt;makePaginator(
$data['total'] ?? 0, // 传入总记录数
$data['subjects'] ?? [] // 传入数据二维数组
);
}</code></pre>
<h4>不分页</h4>
<p>如果不需要分页,则直接返回一个<code>array</code>或<code>Illuminate\Support\Collection</code>类型值即可。</p>
<pre><code class="language-php"> public function get(Grid\Model $model)
{
return [
['id' =&gt; 1, 'name' =&gt; 'n1'],
['id' =&gt; 2, 'name' =&gt; 'n2']
];
}</code></pre>
<p>注意,<code>grid</code>需要禁用分页</p>
<pre><code class="language-php">$grid-&gt;disablePagination()</code></pre>
<h3>edit</h3>
<p>此接口要求返回表单编辑页面的数据,用于显示数据表单编辑页面,需要返回<code>array</code>类型值。</p>
<pre><code class="language-php"> public function edit(Form $form): array
{
// 获取数据主键值
$id = $form-&gt;getKey();
return ['id' =&gt; 1, 'name' =&gt; 'n1'];
}</code></pre>
<h3>detail</h3>
<p>此接口要求返回数据详情页面的数据,用于显示数据详情,需要返回<code>array</code>类型值。</p>
<pre><code class="language-php"> public function detail(Show $show): array
{
// 获取数据主键值
$id = $show-&gt;getId();
return ['id' =&gt; 1, 'name' =&gt; 'n1'];
}</code></pre>
<h3>store</h3>
<p>此接口用于新增一条记录,可以返回<code>int</code>、<code>string</code>或<code>bool</code>类型值。</p>
<pre><code class="language-php"> public function store(Form $form)
{
// 获取待新增的数据
$attributes = $form-&gt;updates();
// 执行你的新增逻辑
// 返回新增记录id或bool值
return 1;
}</code></pre>
<h3>updating</h3>
<p>此接口用于数据表单修改数据时查询原始记录,需要返回<code>array</code>或<code>Model</code>类型值。</p>
<p>> {tip} 此接口只有某些特殊字段会用到,如图片、文件上传字段,当更改了图片或文件时可以根据这个接口查出的数据删除旧文件。所以如果你的表单中没有用到此类特殊字段,此接口可以返回一个空数组。</p>
<pre><code class="language-phpjie"> public function updating(Form $form)
{
// 获取数据主键值
$id = $form-&gt;getKey();
return ['id' =&gt; 1, 'name' =&gt; 'n1'];
}</code></pre>
<h3>update</h3>
<p>此接口用于数据表单修改记录,可以返回<code>int</code>、<code>string</code>或<code>bool</code>类型值。</p>
<pre><code class="language-php"> public function update(Form $form)
{
// 获取待编辑的数据
$attributes = $form-&gt;updates();
// 执行你的编辑逻辑
// 返回成功
return true;
}</code></pre>
<h3>deleting</h3>
<p>此接口用于删除数据时查询原始记录,需要返回二维数组,或Collection model。</p>
<pre><code class="language-php"> public function deleting(Form $form): array
{
// 当批量删除时id为多个
$id = explode(',', $form-&gt;getKey());
// 执行你的逻辑
// 注意这里需要返回二维数组
return [
['id' =&gt; 1, 'name' =&gt; 'h1'],
];
// 也可以返回collection
return Modell::find($id);
}</code></pre>
<h3>destroy</h3>
<p>单行/批量删除数据方法,成功返回<code>true</code>,失败返回<code>false</code>。</p>
<pre><code class="language-php"> public function destroy(Form $form, array $deletingData)
{
// 当批量删除时id为多个
$id = explode(',', $form-&gt;getKey());
// $deletingData 是 getDataWhenDeleting 接口返回的数据
// 执行你的逻辑
return true;
}</code></pre>
<h2>TreeRepository接口</h2>
<h3>getPrimaryKeyColumn</h3>
<p>此接口用于返回数据的主键字段名称,需要返回<code>string</code>类型值。</p>
<pre><code class="language-php"> public function getPrimaryKeyColumn()
{
return $this-&gt;getKeyName();
}</code></pre>
<h3>getParentColumn</h3>
<p>此接口用于返回数据的父ID字段名称,需要返回<code>string</code>类型值。</p>
<pre><code class="language-php"> public function getParentColumn()
{
return 'parent_id';
}</code></pre>
<h3>getTitleColumn</h3>
<p>此接口用于返回数据标题字段名称,需要返回<code>string</code>类型值。</p>
<pre><code class="language-php"> public function getTitleColumn()
{
return 'title';
}</code></pre>
<h3>getOrderColumn</h3>
<p>此接口用于返回数据排序字段名称,需要返回<code>string</code>类型值。</p>
<p>> {tip} 此字段不是必须的,如果你的数据不支持或不需要排序,请返回空值!</p>
<pre><code class="language-php"> public function getOrderColumn()
{
return 'order';
}</code></pre>
<h3>saveOrder</h3>
<p>此接口用于保存层级数据的排序,并且接收两个参数</p>
<ul>
<li><code>$tree</code> <code>array</code> 此字段是一个已分好层级的数组</li>
<li><code>$parentId</code> <code>int</code> 此字段主要用于递归时传递父ID使用</li>
</ul>
<p>> {tip} 如果你的数据不支持 <code>MySQL</code>,可参考 <code>Dcat\Admin\Traits\ModelTree::saveOrder</code> 方法自行实现。</p>
<pre><code class="language-php">$tree = [
[
'id' =&gt; 1,
'title' =&gt; 'title',
'parent_id' =&gt; 0,
'children' =&gt; [
[
'id' =&gt; 2,
'title' =&gt; 'child1',
'parent_id' =&gt; 1,
],
[
'id' =&gt; 3,
'title' =&gt; 'child2',
'parent_id' =&gt; 1,
],
],
]
];
// 保存排序,内层逻辑请自行实现
$repository-&gt;saveOrder($tree); </code></pre>
<h3>withQuery</h3>
<p>此接口需结合 <code>toTree</code> 接口使用,接收一个参数:主要用于设置数据查询操作的相关回调或参数。</p>
<pre><code class="language-php">&lt;?php
use Dcat\Admin\Contracts\Repository;
use Dcat\Admin\Contracts\TreeRepository;
use Dcat\Admin\Support\Helper;
class Category implements Repository, TreeRepository
{
protected $queryCallbacks = [];
public function withQuery($queryCallback)
{
$this-&gt;queryCallbacks[] = $queryCallback;
return $this;
}
public function toTree()
{
// 这里演示的代码只是为了说明 withQuery 方法的作用
$client = ...;
foreach ($this-&gt;queryCallbacks as $callback) {
$callback($client);
}
return Helper::buildNestedArray($client-&gt;get());
}
} </code></pre>
<h3>toTree</h3>
<p>此接口主要用于查询数据并分好层级返回,需要返回 <code>array</code> 类型值。</p>
<pre><code class="language-php"> public function toTree()
{
$client = ...;
foreach ($this-&gt;queryCallbacks as $callback) {
$callback($client);
}
return Helper::buildNestedArray($client-&gt;get());
}</code></pre>
<h2>模型</h2>
<p><code>Dcat Admin</code>已经内置了对<code>Eloquent model</code>的支持,如果你的数据源是支持<code>Model</code>的,那么只需继承<code>Dcat\Admin\Repositories\EloquentRepository</code>类即可实现对数据的<code>CURD</code>操作,如:</p>
<pre><code class="language-php">&lt;?php
namespace App\Admin\Repositories;
use Dcat\Admin\Repositories\EloquentRepository;
use App\Models\Movie as MovieModel;
class Movie extends EloquentRepository
{
// 这里设置你的模型类名即可
protected $eloquentClass = MovieModel::class;
// 通过这个方法可以指定查询的字段,默认&quot;*&quot;
public function getGridColumns()
{
return [$this-&gt;getKeyName(), 'name', 'title', 'created_at'];
}
// 通过这个方法可以指定表单页查询的字段,默认&quot;*&quot;
public function getFormColumns()
{
return [$this-&gt;getKeyName(), 'name', 'title', 'created_at'];
}
// 通过这个方法可以指定数据详情页查询的字段,默认&quot;*&quot;
public function getDetailColumns()
{
return ['*'];
}
}</code></pre>
<h2>QueryBuilder</h2>
<p>如果你的数据支持<code>QueryBuilder</code>查询,但不方便建模型类(比如需要动态查表数据),则可以继承<code>Dcat\Admin\Repositories\QueryBuilderRepository</code>类。</p>
<p>> {tip} 注意,<code>QueryBuilderRepository</code>默认是不支持<code>Model</code>的关联模型、软删除、模型树以及字段排序等功能,如果需要这些功能,请自定实现上述相关接口即可。</p>
<pre><code class="language-php">&lt;?php
namespace App\Admin\Repositories;
use Dcat\Admin\Repositories\QueryBuilderRepository;
class MyRepository extends QueryBuilderRepository
{
// 设置你的主键名称
protected $keyName = 'id';
// 设置创建时间字段
protected $createdAtColumn = 'created_at';
// 设置更新时间字段
protected $updatedAtColumn = 'updated_at';
// 返回表名
public function getTable()
{
return 'your_table_name';
}
// 返回你的主键名称
public function getKeyName()
{
return $this-&gt;keyName;
}
// 通过这个方法可以指定查询的字段,默认&quot;*&quot;
public function getGridColumns()
{
return [$this-&gt;getKeyName(), 'name', 'title', 'created_at'];
}
// 通过这个方法可以指定表单页查询的字段,默认&quot;*&quot;
public function getFormColumns()
{
return [$this-&gt;getKeyName(), 'name', 'title', 'created_at'];
}
// 通过这个方法可以指定数据详情页查询的字段,默认&quot;*&quot;
public function getDetailColumns()
{
return ['*'];
}
}</code></pre>