Issues (54)

src/MenuMaker.php (3 issues)

1
<?php
2
3
namespace PhpCollective\MenuMaker;
4
5
use Cache;
6
use Route;
7
use Kalnoy\Nestedset\Collection;
8
use PhpCollective\MenuMaker\Storage\Role;
9
use PhpCollective\MenuMaker\Storage\Menu;
10
11
trait MenuMaker
12
{
13
    protected $section;
14
15
    /**
16
     * The group that associates with the group.
17
     */
18
    public function roles()
19
    {
20
        return $this->belongsToMany(Role::class, 'pcmm_role_user', 'user_id', 'role_id');
0 ignored issues
show
It seems like belongsToMany() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

20
        return $this->/** @scrutinizer ignore-call */ belongsToMany(Role::class, 'pcmm_role_user', 'user_id', 'role_id');
Loading history...
21
    }
22
23
    public function menus($alias)
24
    {
25
        $this->section = Menu::whereAlias($alias)->first();
26
27
        if (! $this->section) {
28
            return collect([]);
29
        }
30
        return $this->getMenuItemsWithActive();
31
32
    }
33
34
    protected function getMenuItemsWithActive()
35
    {
36
        $currentRoute = Route::currentRouteName();
37
        $currentPath = request()->path();
38
        $menus = $this->getMenuItems();
39
40
        $traverse = function ($menus) use (&$traverse, $currentRoute, $currentPath) {
41
            $checked = collect([]);
42
            foreach ($menus as $key => $menu) {
43
                $menu['children'] = $traverse($menu['children'], $currentRoute, $currentPath);
44
                if((is_array($menu['routes']) && in_array($currentRoute, $menu['routes']))
45
                    || $currentPath === $menu['link']
46
                    || $menu['children']->where('active', true)->count())
47
                {
48
                    $menu['active'] = true;
49
                }
50
                $checked->put($key, new MenuItem($menu));
51
            }
52
53
            return $checked;
54
        };
55
56
        return $traverse($menus);
57
    }
58
59
    protected function getMenuItems()
60
    {
61
        return Cache::rememberForever('menus.section.' . optional($this->section)->id . '.user.' . $this->id, function () {
62
            return $this->admin()
63
                ? $this->getAdminMenuItems()
64
                : $this->getUserMenuItems();
65
        });
66
    }
67
68
    private function getAdminMenuItems()
69
    {
70
        $this->section->load([
71
            'descendants' => function ($query) {
72
                $query->visible();
73
            }
74
        ]);
75
76
        return self::buildTree($this->section->descendants, $this->section->id);
77
    }
78
79
    private function getUserMenuItems()
80
    {
81
        $this->section->load([
82
            'descendants' => function ($query) {
83
                $query->with([
84
                    'ancestors' => function ($query) {
85
                        $query->descendantsOf($this->section);
86
                    }
87
                ])->select('pcmm_menus.*')
88
                    ->leftJoin('pcmm_menu_role', 'pcmm_menus.id', '=', 'pcmm_menu_role.menu_id')
89
                    ->leftJoin('pcmm_roles', 'pcmm_menu_role.role_id', '=', 'pcmm_roles.id')
90
                    ->leftJoin('pcmm_role_user', 'pcmm_roles.id', '=', 'pcmm_role_user.role_id')
91
                    ->leftJoin($this->getTable(), 'pcmm_role_user.user_id', '=', $this->getTable() . '.id')
0 ignored issues
show
It seems like getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

91
                    ->leftJoin($this->/** @scrutinizer ignore-call */ getTable(), 'pcmm_role_user.user_id', '=', $this->getTable() . '.id')
Loading history...
92
                    ->visible()
93
                    ->where(function ($query) {
94
                        $query->public()
95
                            ->orWhere(function ($query) {
96
                                $query->where('pcmm_menus.privilege', 'PROTECTED')
97
                                    ->where('pcmm_roles.is_active', true)
98
                                    ->where($this->getTable() . '.id', $this->id);
99
                            });
100
                    });
101
            }
102
        ]);
103
104
        $items = $this->section->descendants->reject(function ($item) {
105
            return $item->ancestors->contains('privilege', 'PRIVATE')
106
                || $item->ancestors->contains('visible', false);
107
        });
108
109
        $menus = collect([]);
110
        foreach ($items as $last) {
111
            $item = self::buildPartialTree($last->ancestors, $last);
112
            if ($menus->has($item['id'])) {
113
                $menus = self::mergeTree($menus, $item);
114
            } else {
115
                $menus->put($item['id'], $item);
116
            }
117
        }
118
119
        return $menus;
120
    }
121
122
    protected static function mergeTree($menus, $item)
123
    {
124
        if (isset($menus[$item['id']])) {
125
            $childItem = $item['children']->first();
126
            if ($menus[$item['id']]['children']->has($childItem['id'])) {
127
                $children = $menus[$item['id']]['children'];
128
                $menus[$item['id']]['children'] = self::mergeTree($children, $childItem);
129
            } else {
130
                $menus[$item['id']]['children']->put($childItem['id'], $childItem);
131
            }
132
        }
133
        return $menus;
134
    }
135
136
    protected static function buildPartialTree(Collection $nodes, $lastChild)
137
    {
138
        if ($nodes->count()) {
139
            $node = $nodes->shift();
140
            $element = self::prepareMenuItem($node);
141
            $child = $nodes->count()
142
                ? self::buildPartialTree($nodes, $lastChild)
143
                : self::prepareMenuItem($lastChild);
144
145
            $element['children']->put(
146
                $child['id'], $child
147
            );
148
        } else {
149
            $element = self::prepareMenuItem($lastChild);
150
        }
151
        return $element;
152
    }
153
154
    protected static function buildTree(Collection $nodes, $parent_id = 0)
155
    {
156
        $branch = collect([]);
157
158
        foreach ($nodes as $node) {
159
            if ($node->parent_id == $parent_id) {
160
161
                $element = self::prepareMenuItem($node);
162
163
                $children = self::buildTree($node->children()->visible()->get(), $node->id);
164
                if ($children) {
165
                    $element['children'] = $children;
166
                }
167
168
                $branch->put($element['id'], $element);
169
            }
170
        }
171
172
        return $branch;
173
    }
174
175
    protected static function prepareMenuItem(Menu $node)
176
    {
177
        return [
178
            'id'       => $node->id,
179
            'name'     => $node->name,
180
            'alias'   => $node->alias,
181
            'routes'   => $node->routes,
182
            'link'     => $node->link,
183
            'icon'     => $node->icon,
184
            'class'    => $node->class,
185
            'attr'     => $node->attr,
186
            'active'   => false,
187
            'children' => collect([])
188
        ];
189
    }
190
191
    public function admin()
192
    {
193
        return $this->roles()->admin()->exists();
194
    }
195
196
    public function approve($alias)
197
    {
198
        return $this->admin() || $this->whereHas('roles.menus', function ($query) use ($alias) {
0 ignored issues
show
It seems like whereHas() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

198
        return $this->admin() || $this->/** @scrutinizer ignore-call */ whereHas('roles.menus', function ($query) use ($alias) {
Loading history...
199
                $query->whereAlias($alias);
200
            })->exists();
201
    }
202
203
    public function authorize($request)
204
    {
205
        $route = explode_route($request->route());
206
        return $this->whereHas('roles.menus.permissions', function ($query) use ($route) {
207
            return $query->ofRoute($route);
208
        })->exists();
209
    }
210
}
211