Passed
Branch main (f9aaf7)
by Jonathan
14:43
created

HookService::all()   B

Complexity

Conditions 7
Paths 1

Size

Total Lines 37
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 26
nc 1
nop 1
dl 0
loc 37
rs 8.5706
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * webtrees-lib: MyArtJaub library for webtrees
5
 *
6
 * @package MyArtJaub\Webtrees
7
 * @subpackage Hooks
8
 * @author Jonathan Jaubart <[email protected]>
9
 * @copyright Copyright (c) 2011-2021, Jonathan Jaubart
10
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License, version 3
11
 */
12
13
declare(strict_types=1);
14
15
namespace MyArtJaub\Webtrees\Module\Hooks\Services;
16
17
use Fisharebest\Webtrees\Registry;
18
use Fisharebest\Webtrees\Module\ModuleInterface;
19
use Fisharebest\Webtrees\Services\ModuleService;
20
use Illuminate\Database\Capsule\Manager as DB;
21
use Illuminate\Support\Collection;
22
use MyArtJaub\Webtrees\Contracts\Hooks\HookCollectorInterface;
23
use MyArtJaub\Webtrees\Contracts\Hooks\HookInterface;
24
use MyArtJaub\Webtrees\Contracts\Hooks\HookServiceInterface;
25
use MyArtJaub\Webtrees\Contracts\Hooks\ModuleHookSubscriberInterface;
26
use stdClass;
27
28
/**
29
 * Service for accessing hooks subscribed by modules.
30
 */
31
class HookService implements HookServiceInterface
32
{
33
    private ModuleService $module_service;
34
35
    /**
36
     * Constructor for HookService
37
     *
38
     * @param ModuleService $module_service
39
     */
40
    public function __construct(ModuleService $module_service)
41
    {
42
        $this->module_service = $module_service;
43
    }
44
45
    /**
46
     * {@inheritDoc}
47
     * @see \MyArtJaub\Webtrees\Contracts\Hooks\HookServiceInterface::use()
48
     */
49
    public function use(string $hook_interface): ?HookCollectorInterface
50
    {
51
        return $this->all()->get($hook_interface);
52
    }
53
54
55
    /**
56
     * Find a hook collector by its name, with or without the disabled ones.
57
     *
58
     * @template THook of HookInterface
59
     * @param string $hook_name
60
     * @return HookCollectorInterface<THook>|null
61
     */
62
    public function find(string $hook_name, bool $include_disabled = false): ?HookCollectorInterface
63
    {
64
        return $this->all($include_disabled)
65
            ->first(fn(HookCollectorInterface $hook_collector) => $hook_collector->name() === $hook_name);
66
    }
67
68
    /**
69
     * Get all hook collectors subscribed by modules, with hooks ordered, with or without the disabled ones.
70
     *
71
     * @template THook of HookInterface
72
     * @param bool $include_disabled
73
     * @return Collection<string, HookCollectorInterface<THook>>
74
     */
75
    public function all(bool $include_disabled = false): Collection
76
    {
77
        return Registry::cache()->array()->remember('all-hooks', function () use ($include_disabled): Collection {
78
            $hooks_info = DB::table('maj_hook_order')
79
                ->get()
80
                ->groupBy(['majho_hook_name', 'majho_module_name']);
81
82
            $hooks = $this->module_service
83
                ->findByInterface(ModuleHookSubscriberInterface::class, $include_disabled)
84
                ->flatMap(fn(ModuleHookSubscriberInterface $module) => $module->listSubscribedHooks());
85
86
            $hook_collectors = collect();
87
            $hook_instances = collect();
88
            foreach ($hooks as $hook) {
89
                if (!($hook instanceof HookInterface)) {
90
                    continue;
91
                }
92
                if ($hook instanceof HookCollectorInterface) {
93
                    $hook_collectors->put($hook->hookInterface(), $hook);
94
                } else {
95
                    $hook_instances->add($hook);
96
                }
97
            }
98
99
            foreach ($hook_collectors as $hook_interface => $hook_collector) {
100
                $hook_info = $hooks_info->get($hook_collector->name()) ?? collect();
101
                foreach (
102
                    $hook_instances->filter(
103
                        fn(HookInterface $hook): bool => $hook instanceof $hook_interface
104
                    ) as $hook_instance
105
                ) {
106
                    $hook_module_info = $hook_info->get($hook_instance->module()->name(), collect())->first();
107
                    $hook_order = $hook_module_info instanceof stdClass ? (int) $hook_module_info->majho_hook_order : 0;
108
                    $hook_collector->register($hook_instance, $hook_order);
109
                }
110
            }
111
            return $hook_collectors;
112
        });
113
    }
114
115
    /**
116
     * Update the order of the modules implementing a hook in the database.
117
     *
118
     * @template THook of HookInterface
119
     * @param HookCollectorInterface<THook> $hook_collector
120
     * @param ModuleInterface $module
121
     * @param int $order
122
     * @return int
123
     */
124
    public function updateOrder(HookCollectorInterface $hook_collector, ModuleInterface $module, int $order): int
125
    {
126
        return DB::table('maj_hook_order')
127
            ->upsert([
128
                'majho_module_name' =>  $module->name(),
129
                'majho_hook_name'   =>  $hook_collector->name(),
130
                'majho_hook_order'  =>  $order
131
            ], ['majho_module_name', 'majho_hook_name'], ['majho_hook_order']);
132
    }
133
}
134