@這是小豪的第八篇文章
這兩天在折騰組織架構的人員選擇器,很難受啊,每次遇到這種需要用到遞迴的計算腦袋就轉不過來,不過好在還是折騰出來了,今天給大家介紹一下到底折騰出來了啥,優不優雅,哈哈。
準備
做什麼?組織架構的人員選擇器的 API 介面,其中組織架構的層級是無下限的,姑且當做是無限極的吧。。。無限極最近貌似有點火呀,哈哈。
我們先來看一下表結構,直接從模型中看吧:
class Department extends Model
{
use SoftDeletes;
protected $fillable = [
'parent_id', 'name', 'alias', 'level',
];
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function children()
{
return $this->hasMany(__CLASS__, 'parent_id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function users()
{
return $this->hasMany(User::class);
}
}
模型中宣告瞭兩種模型關聯,部門子集以及部門員工,如果對模型關聯不太熟悉的建議看一下之前的文章,《如何更快的找到自己所需的模型關聯型別?》
再來看一下控制器:
class DepartmentController extends Controller
{
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection|\Illuminate\Support\Collection
*/
public function index(Request $request)
{
if ($request->has('tree')) {
return DepartmentResource::tree();
}
return new DepartmentResource(Department::where($request->all())->get());
}
}
因為樹形結構不是必選項,所以在有接收到 tree
引數的時候才進行處理。
最後看一下 API 資源:
class DepartmentResource extends JsonResource
{
}
不熟悉 API 資源的,建議看一下之前的又一篇文章,哈哈。《如何優雅的去處理 API 資料格式》,我都佩服自己打廣告的能力了,哈哈。
開始了噢
1. 我們先在 DepartmentResource
中定義一個靜態方法 tree
,並獲取所有部門資料
public static function tree()
{
// 我這裡預設把 users 給加上去了,大家可以根據自己的需求來決定
$departments = Department::with('users')->get();
}
2. 現在我們需要對部門所有的資料進行處理了,這是最頭疼的。。。 我們來看一下小豪是如何處理的
/**
* @param \Illuminate\Support\Collection $departments
* @param \Illuminate\Support\Collection $parents
*
* @return \Illuminate\Support\Collection
*/
protected static function departmentsTree(Collection $departments, Collection $parents)
{
$departments->map(function ($department, $key) use ($departments, $parents) {
$department->children = \collect([]);
if (empty($department->parent_id)) {
$parents->push($department);
$departments->forget($key);
}
$parents->map(function ($parentDepartment, $parentKey) use ($key, $department, $departments, $parents) {
if ($department->parent_id == $parentDepartment->id) {
$parents->get($parentKey)->children->push($department);
$departments->forget($key);
}
});
});
$parents->map(function ($parentDepartment, $key) use ($departments, $parents) {
if ($parentDepartment->children->isNotEmpty()) {
$parents->get($key)->children = self::departmentTree($departments, $parentDepartment->children);
}
});
return $parents;
}
因為
Department::with('users')->get()
獲取的是一個集合,所以我們都是用集合的方式進行處理的。
-
我們接收了兩個引數:
$departments
和$parents
,第一個引數就不多說了,第二個引數用來裝最外層的部門,也就是一級部門(父級 id 為 0 的部門)。 -
我們先對所有部門進行遍歷,給每一個部門初始化一個
children
空集合,然後將一級部門裝進$parents
中,並從$departments
剔除已經使用過的department
。 -
對
$parents
進行遍歷,為department
找到指定父級,並push
到children
集合中,同樣剔除已經使用過的department
。 -
上面的操作只進行了兩層,現在到了關鍵點了,我們對已經經過一層篩選的
$parents
進行遍歷,將那些存在children
的挑出來,然後繼續進行上面的操作,同時第二個引數為$parentDepartment->children
,反覆處理之後,就能得到最終的結果啦。
不知道說清楚沒有,哈哈。
3. 現在來呼叫一下:
/**
* @return \Illuminate\Support\Collection
*/
public static function tree()
{
$departments = Department::with(\request()->includes())->get();
return self::departmentTree($departments, \collect([]));
}
4. 大公告成,哈哈。
不過還沒完,我們來看一下基於模型的樹狀結構該怎麼去寫,你想不到的優雅,哈哈。
優雅的方式開始
我們在模型中新增這樣一個方法:
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function subDepartments()
{
return $this->children()->with('subDepartments');
}
Api 資源中處理:
class DepartmentResource extends JsonResource
{
public function __construct(\Illuminate\Database\Eloquent\Model $resource)
{
parent::__construct($resource);
return $resource->subDepartment;
}
}
呼叫:
class DepartmentController extends Controller
{
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
*
* @return \Illuminate\Http\Resources\Json\AnonymousResourceCollection|\Illuminate\Support\Collection
*/
public function index(Request $request)
{
return DepartmentResource::collection(Department::where($request->all())->get());
}
}
這樣就 ok 了,是不是大開眼界,反正我是的,哈哈。不過這做有個弊病就是在不指定部門的時候,所有的部門全部展示出來了,不過都可以靈活的運用啦。
結束語
寫的很簡陋,大家如果看出可以最佳化的,或者有錯誤的地方,指正一下哈,共同進步呀,哈哈。
本作品採用《CC 協議》,轉載必須註明作者和本文連結