Passed
Push — master ( cb0392...409c3f )
by Andreas
09:17
created

midcom_helper_nav_node::get_component_nap()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0987

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 1
dl 0
loc 12
ccs 7
cts 9
cp 0.7778
crap 3.0987
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package midcom.helper.nav
4
 * @author CONTENT CONTROL http://www.contentcontrol-berlin.de/
5
 * @copyright CONTENT CONTROL http://www.contentcontrol-berlin.de/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
/**
10
 * @property array $subnodes
11
 * @package midcom.helper.nav
12
 */
13
class midcom_helper_nav_node extends midcom_helper_nav_item
14
{
15
    private ?midcom_db_topic $topic = null;
16
17
    private int $topic_id;
18
19 142
    public function __construct($topic)
20
    {
21 142
        if ($topic instanceof midcom_db_topic) {
22 61
            $this->topic = $topic;
23 61
            $this->topic_id = $topic->id;
24
        } else {
25 93
            $this->topic_id = (int) $topic;
26
        }
27
    }
28
29 131
    public function is_readable_by(string $user_id) : bool
30
    {
31 131
        return (   !$user_id
32 131
                || !$this->guid
33 131
                || midcom::get()->auth->acl->can_do_byguid('midgard:read', $this->guid, midcom_db_topic::class, $user_id));
34
    }
35
36 10
    public function get_subnodes() : array
37
    {
38 10
        if (!isset($this->subnodes)) {
39 10
            if ($this->topic_id == 0) {
40
                $this->subnodes = [];
41
            } else {
42
                // Use midgard_collector to get the subnodes
43 10
                $mc = midcom_db_topic::new_collector('up', $this->topic_id);
44 10
                $mc->add_constraint('name', '<>', '');
45 10
                $mc->add_order('metadata.score', 'DESC');
46 10
                $mc->add_order('metadata.created');
47
48
                //we always write all the subnodes to cache and filter for ACLs after the fact
49 10
                midcom::get()->auth->request_sudo('midcom.helper.nav');
50 10
                $this->subnodes = $mc->get_values('id');
51 10
                midcom::get()->auth->drop_sudo();
52
            }
53 10
            $this->get_cache()->put_node($this->topic_id, $this->get_data());
54
        }
55
56 10
        return $this->subnodes;
57
    }
58
59
    /**
60
     * @return midcom_helper_nav_leaf[]
61
     */
62 43
    public function get_leaves() : array
63
    {
64 43
        $leaves = $this->get_cache()->get_leaves("{$this->id}-leaves");
65 43
        $from_cache = (null !== $leaves);
66 43
        if (!$from_cache) {
67 43
            debug_add('The leaves have not yet been loaded from the database, we do this now.');
68
69
            // we always write all the leaves to cache and filter for ACLs after the fact
70 43
            midcom::get()->auth->request_sudo('midcom.helper.nav');
71 43
            if ($nap = $this->get_component_nap($this->object)) {
72 43
                $leaves = $nap->get_leaves();
73
            }
74 43
            midcom::get()->auth->drop_sudo();
75
        }
76
77 43
        $result = [];
78 43
        foreach ($leaves as $id => $leaf) {
79 31
            $leaf = new midcom_helper_nav_leaf($this, $leaf, $id, $from_cache);
80 31
            $result[$leaf->id] = $leaf;
81
        }
82 43
        if (!$from_cache) {
83 43
            $this->write_leaves_to_cache($result);
84
        }
85 43
        return $result;
86
    }
87
88
    /**
89
     * Writes the passed leaves to the cache, assigning them to the specified node.
90
     *
91
     * The function will bail out on any critical error. Data inconsistencies will be
92
     * logged and overwritten silently otherwise.
93
     *
94
     * @param midcom_helper_nav_leaf[] $leaves The leaves to store in the cache.
95
     */
96 43
    private function write_leaves_to_cache(array $leaves)
97
    {
98 43
        if (!$this->get_cache()->get_node($this->id)) {
99 35
            debug_add("NAP Caching Engine: Tried to update the topic {$this->name} (#{$this->object->id}) "
100 35
            . 'which was supposed to be in the cache already, but failed to load the object from the database.
101 35
                  Aborting write_to_cache, this is a critical cache inconsistency.', MIDCOM_LOG_WARN);
102 35
            return;
103
        }
104 8
        $cachedata = [];
105 8
        foreach ($leaves as $leaf) {
106 1
            $cachedata[$leaf->id] = $leaf->write_to_cache();
107
        }
108
109 8
        debug_add('Writing ' . count($cachedata) . ' leaves to the cache.');
110 8
        $this->get_cache()->put_leaves("{$this->id}-leaves", $cachedata);
111
    }
112
113 142
    protected function prepare_data() : array
114
    {
115 142
        $data = $this->get_cache()->get_node($this->topic_id);
116
117 142
        if (!$data) {
118 131
            midcom::get()->auth->request_sudo('midcom.helper.nav');
119 131
            $data = $this->load_data();
120 131
            midcom::get()->auth->drop_sudo();
121
122 131
            if ($data === null) {
123 15
                debug_add('We got null for this node, so we do not have any NAP information, returning directly.');
124 15
                return [];
125
            }
126
127 121
            $this->get_cache()->put_node($data[MIDCOM_NAV_ID], $data);
0 ignored issues
show
Bug introduced by
It seems like $data[MIDCOM_NAV_ID] can also be of type midcom_helper_configuration; however, parameter $key of midcom_services_cache_module_nap::put_node() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

127
            $this->get_cache()->put_node(/** @scrutinizer ignore-type */ $data[MIDCOM_NAV_ID], $data);
Loading history...
128 121
            debug_add("Added the ID {$data[MIDCOM_NAV_ID]} to the cache.");
129
        }
130
131 132
        return $data;
132
    }
133
134 131
    private function load_data() : ?array
135
    {
136 131
        if (empty($this->topic)) {
137 85
            $topic = new midcom_core_dbaproxy($this->topic_id, midcom_db_topic::class);
138
        } else {
139 54
            $topic = $this->topic;
140
        }
141
142
        // Retrieve a NAP instance
143 131
        $nap = $this->get_component_nap($topic);
0 ignored issues
show
Bug introduced by
It seems like $topic can also be of type midcom_core_dbaproxy; however, parameter $topic of midcom_helper_nav_node::get_component_nap() does only seem to accept midcom_db_topic, maybe add an additional type check? ( Ignorable by Annotation )

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

143
        $nap = $this->get_component_nap(/** @scrutinizer ignore-type */ $topic);
Loading history...
144 131
        if (!$nap) {
145 15
            return null;
146
        }
147
148
        // Get the node data and verify this is a node that actually has any relevant NAP
149
        // information. Internal components which don't have
150
        // a NAP interface yet return null here, to be exempt from any NAP processing.
151 121
        $data = $nap->get_node();
152 121
        if ($data === null) {
153
            debug_add("The component '{$topic->component}' did return null for the topic {$topic->id}, indicating no NAP information is available.");
0 ignored issues
show
Bug Best Practice introduced by
The property component does not exist on midcom_core_dbaproxy. Since you implemented __get, consider adding a @property annotation.
Loading history...
154
            return null;
155
        }
156
157
        // Now complete the node data structure
158 121
        $data[MIDCOM_NAV_NAME] = trim($data[MIDCOM_NAV_NAME]) == '' ? $topic->name : $data[MIDCOM_NAV_NAME];
0 ignored issues
show
Bug introduced by
It seems like $data[MIDCOM_NAV_NAME] can also be of type midcom_helper_configuration; however, parameter $string of trim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

158
        $data[MIDCOM_NAV_NAME] = trim(/** @scrutinizer ignore-type */ $data[MIDCOM_NAV_NAME]) == '' ? $topic->name : $data[MIDCOM_NAV_NAME];
Loading history...
Bug Best Practice introduced by
The property name does not exist on midcom_core_dbaproxy. Since you implemented __get, consider adding a @property annotation.
Loading history...
159 121
        $data[MIDCOM_NAV_URL] = $topic->name . '/';
160 121
        $data[MIDCOM_NAV_GUID] = $topic->guid;
161 121
        $data[MIDCOM_NAV_ID] = $topic->id;
162 121
        $data[MIDCOM_NAV_NODEID] = $topic->up;
0 ignored issues
show
Bug Best Practice introduced by
The property up does not exist on midcom_core_dbaproxy. Since you implemented __get, consider adding a @property annotation.
Loading history...
163 121
        $data[MIDCOM_NAV_TYPE] = 'node';
164 121
        $data[MIDCOM_NAV_SCORE] = $topic->metadata->score;
0 ignored issues
show
Bug Best Practice introduced by
The property metadata does not exist on midcom_core_dbaproxy. Since you implemented __get, consider adding a @property annotation.
Loading history...
165 121
        $data[MIDCOM_NAV_COMPONENT] = $topic->component;
166 121
        $data[MIDCOM_NAV_SORTABLE] = true;
167 121
        $data[MIDCOM_NAV_OBJECT] = $topic;
168 121
        $data[MIDCOM_NAV_NOENTRY] ??= (bool) $topic->metadata->get('navnoentry');
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

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

168
        $data[MIDCOM_NAV_NOENTRY] ??= (bool) $topic->metadata->/** @scrutinizer ignore-call */ get('navnoentry');

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...
169
170 121
        return $data;
171
    }
172
173
    /**
174
     * @param midcom_db_topic $topic
175
     */
176 151
    private function get_component_nap($topic) : ?midcom_baseclasses_components_navigation
177
    {
178 151
        if (!$topic->component) {
179 15
            return null;
180
        }
181 141
        $interface = midcom::get()->componentloader->get_interface_class($topic->component);
182 141
        $nap = $interface->get_nap_instance();
183 141
        if (!$nap->set_object($topic)) {
184
            debug_add("Could not set the NAP instance of '{$topic->component}' to the topic {$topic->id}.", MIDCOM_LOG_ERROR);
185
            return null;
186
        }
187 141
        return $nap;
188
    }
189
}
190