我在Laravel中实现了一个系统,用户可以是多个“商店”的管理员。一个商店可以有多个属于该商店的用户。这是我的简化表结构-
用户
user_stores
商店
我的问题是检查具有经理数据透视标记的user
可以管理谁。我有一个解决方案,但不确定是否最佳。我希望查询尽可能精简。这是我当前的解决方案-
protected $manageable_users;
public function getManageableUserIds () {
if(!is_array($this->manageable_users)) {
// get our store id's that we manage
$manages = DB::table('user_stores')
->where('user_id', $this->id)
->where('manager', true)
->select('store_id');
// make a join so we can get our data
$this->manageable_users = DB::table('user_stores AS d2')
->joinSub($manages, 'stores', function ($join) {
$join->on('d2.store_id', '=', 'stores.dealership_id');
})->distinct()->pluck('d2.user_id')->toArray();
}
return $this->manageable_users;
}
因此,我在这里要做的是获取经理可能管理的所有用户ID的数组。然后,我将其存储为一个受保护的变量,以便在同一请求上可以在同一请求中多次执行此检查,而无需进行多次查询。
然后我有一个名为canManage
的单独方法,该方法检查当前用户对象是否可以实际管理传递的用户-
public function canManage(User $user) {
// check if our user is manageable
return in_array($user->id, $this->getManageableUserIds(), true);
}
现在我知道Laravel非常聪明,由于某种原因,我觉得这不是最好的解决方案。再加上我不希望它在数据库上过于密集,因为最终会出现很多{{ 1}}和users
。
如果没有别的,也许这可能是其他人的解决方案!
谢谢。
答案 0 :(得分:0)
因此,基本上,您想建立一个枢轴关系。相对于您的情况,这是您可以采取的方法:
app / User.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model {
public $table = 'user';
protected $primaryKey 'id';
protected $foreignKey = 'user_id';
public function stores() {
return $this->belongsToMany('App\Store', 'user_stores', 'user_id', 'store_id')->withPivot('manager');
}
}
?>
app / Store.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use App\Store;
class Store extends Model {
public $table = 'store';
protected $primaryKey 'id';
protected $foreignKey = 'store_id';
public function users() {
return $this->belongsToMany('App\User', 'user_stores', 'store_id', 'user_id')->withPivot('manager');
}
}
?>
现在,让我们说您预先知道ID为4
的用户是管理员,并且您希望获得该管理员可以管理的所有用户。
我假设我们在一个控制器中,因为在大多数情况下,这是您所有业务逻辑所在的位置,因此您正在浏览路线/user/4/store/1
,并且我们将显示所有用户列表用户(经理)进行管理。
我还要在路线中添加商店(1
,以明确表明我们希望该商店的所有用户加入,因为您的模型不会禁止经理来管理多个商店。版本发布后,假设经理仅管理一家商店。
app / Http / Controllers / UserStoreController.php
<?php
namespace App\Http\Controllers;
use App\User;
use App\Http\Controllers\Controller;
class UserStoreController extends Controller {
public function show(User $user, Store $store) {
// Grab all the users that are in the store and that are not manager
$managed_users = User::whereHas('stores', function($query) use($store) {
$query->where('manager', false)->where('store_id', $store->id);
})->get();
return view('user.store.index')
->withStore($store)
->withUser($user)
->withManagedUsers($managed_users);
}
}
(注意,访问具有ID的路由可以让我们使用模型注入的功能,这就是为什么我不放置public function show($user_id, $store_id)
的原因,因为Laravel将自动使用User::find($user_id)
和{{ 1}})。
假设经理只能管理一个商店的版本:
app / Http / Controllers / UserController.php
Store::find($store_id)
希望有帮助。
答案 1 :(得分:0)
这是原始的SQL解决方案:
SELECT
a.userid FROM user_stores a
INNER JOIN
user_stores b ON a.store_id = b.store_id
WHERE
a.manager=0 AND b.manager=1;
要将其转换为雄辩的,可以尝试使用DB :: select和/或DB :: raw
数据库表视图也值得考虑。然后,您只需要查询视图即可获得自定义的定制结果,而不是直接获取表。这种解决方案可以促进干净的代码,使您可以继续使用ORM,不需要在应用程序中运行原始SQL,并且可以将所有操作作为原始SQL来获得所有的速度优势。主要的缺点是必须编写这样一个视图并确保它得到维护。与使用触发器几乎一样的缺点。
另一个想法是通过ORM一次拉动整个数据库,然后使用laravel collections过滤数据。在这种情况下,我可能会减少。也可以使用过滤器或列表中的其他任何内容进行流水线处理。当然这是多余的循环,但是与ORM相比,性能折衷真的不好吗?
最后,还值得考虑“任何可行且最易读的”解决方案,然后使用例如redis对其进行缓存。