Completed
Push — master ( a19a47...15bbc5 )
by Arjay
15:59
created

DynamicMenusBuilder   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 101
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 0
loc 101
rs 10
c 0
b 0
f 0
wmc 16
lcom 1
cbo 4

3 Methods

Rating   Name   Duplication   Size   Complexity  
A generateMenu() 0 10 2
A handle() 0 17 2
C registerMenu() 0 43 12
1
<?php
2
3
namespace Yajra\CMS\Http\Middleware;
4
5
use Caffeinated\Menus\Builder;
6
use Caffeinated\Menus\Facades\Menu as MenuFactory;
7
use Closure;
8
use Yajra\CMS\Entities\Menu;
9
use Yajra\CMS\Entities\Navigation;
10
use Yajra\CMS\Entities\Widget;
11
use Yajra\CMS\Repositories\Navigation\Repository;
12
13
class DynamicMenusBuilder
14
{
15
    /**
16
     * @var Repository
17
     */
18
    protected $repository;
19
20
    /**
21
     * Handle an incoming request.
22
     *
23
     * @param  \Illuminate\Http\Request $request
24
     * @param  \Closure $next
25
     * @return mixed
26
     */
27
    public function handle($request, Closure $next)
28
    {
29
        session()->forget('active_menu');
30
31
        if (! $request->is(admin_prefix() . '*')) {
32
            $this->repository = app(Repository::class);
33
            $this->repository->getPublished()->each(function (Navigation $navigation) {
34
                MenuFactory::make($navigation->type, function (Builder $builder) use ($navigation) {
35
                    $navigation->menus->each(function (Menu $menu) use ($builder, &$assignment) {
36
                        $this->generateMenu($builder, $menu);
37
                    });
38
                });
39
            });
40
        }
41
42
        return $next($request);
43
    }
44
45
    /**
46
     * Generate the menu.
47
     *
48
     * @param \Caffeinated\Menus\Builder|\Caffeinated\Menus\Item $menuBuilder
49
     * @param \Yajra\CMS\Entities\Menu $menu
50
     */
51
    protected function generateMenu($menuBuilder, $menu)
52
    {
53
        $subMenu = $this->registerMenu($menuBuilder, $menu);
54
        $menu->children->each(function (Menu $subItem) use ($subMenu) {
55
            $subMenuChild = $this->registerMenu($subMenu, $subItem);
56
            if (count($subItem->children)) {
57
                $this->generateMenu($subMenuChild, $subItem);
0 ignored issues
show
Bug introduced by
It seems like $subMenuChild defined by $this->registerMenu($subMenu, $subItem) on line 55 can also be of type boolean; however, Yajra\CMS\Http\Middlewar...Builder::generateMenu() does only seem to accept object<Caffeinated\Menus...Caffeinated\Menus\Item>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
58
            }
59
        });
60
    }
61
62
    /**
63
     * Register a menu.
64
     *
65
     * @param \Caffeinated\Menus\Builder|\Caffeinated\Menus\Item $menuBuilder
66
     * @param \Yajra\CMS\Entities\Menu $menu
67
     * @return \Caffeinated\Menus\Builder|bool
68
     * @throws \Laracasts\Presenter\Exceptions\PresenterException
69
     */
70
    protected function registerMenu($menuBuilder, Menu $menu)
71
    {
72
        if (! $menuBuilder) {
73
            return false;
74
        }
75
76
        if (! $menu->published) {
77
            return false;
78
        }
79
80
        if ($menu->requiresAuthentication() && ! auth()->check()) {
0 ignored issues
show
Bug introduced by
The method check does only exist in Illuminate\Contracts\Auth\Guard, but not in Illuminate\Contracts\Auth\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
81
            return false;
82
        }
83
84
        if (count($menu->permissions)) {
85
            if ($menu->authorization === 'can') {
86
                foreach ($menu->permissions as $permission) {
87
                    if (! current_user()->can($permission->slug)) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Illuminate\Contracts\Auth\Authenticatable as the method can() does only exist in the following implementations of said interface: Illuminate\Foundation\Auth\User.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
88
                        return false;
89
                    }
90
                }
91
            } else {
92
                $permissions = $menu->permissions->pluck('slug')->toArray();
93
                if (! current_user()->canAtLeast($permissions)) {
0 ignored issues
show
Bug introduced by
The method canAtLeast() does not seem to exist on object<Illuminate\Contracts\Auth\Authenticatable>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
94
                    return false;
95
                }
96
            }
97
        }
98
99
        $item = $menuBuilder->add($menu->title, url($menu->present()->url))
0 ignored issues
show
Bug introduced by
It seems like url($menu->present()->url) targeting url() can also be of type object<Illuminate\Contracts\Routing\UrlGenerator>; however, Caffeinated\Menus\Builder::add() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like url($menu->present()->url) targeting url() can also be of type object<Illuminate\Contracts\Routing\UrlGenerator>; however, Caffeinated\Menus\Item::add() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
100
                            ->attribute('target', $menu->present()->target)
101
                            ->attribute('title', $menu->present()->linkTitle);
102
103
        if ($menu->present()->linkStyle) {
104
            $item->attribute('style', $menu->present()->linkStyle);
105
        }
106
107
        if ($menu->isActive()) {
108
            session()->flash('active_menu', $menu);
109
        }
110
111
        return $item;
112
    }
113
}
114