Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.14% covered (success)
97.14%
68 / 70
75.00% covered (warning)
75.00%
6 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Node
97.14% covered (success)
97.14%
68 / 70
75.00% covered (warning)
75.00%
6 / 8
15
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 byPath
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 byUid
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 byHandle
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 working
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 get
95.45% covered (success)
95.45%
21 / 22
0.00% covered (danger)
0.00%
0 / 1
3
 find
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 foldDraft
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Finder;
6
7use Celema\Core\Exception\HttpBadRequest;
8use Cosray\Bootstrap;
9use Cosray\Cms;
10use Cosray\Context;
11use Cosray\Node\Factory;
12use Cosray\Node\Types;
13use Cosray\Node\Wrapper;
14
15class Node
16{
17    public function __construct(
18        private readonly Context $context,
19        private readonly Cms $cms,
20        private readonly Factory $nodeFactory,
21        private readonly Types $types,
22    ) {}
23
24    public function byPath(
25        string $path,
26        ?bool $deleted = false,
27        ?bool $published = true,
28    ): ?Wrapper {
29        return $this->get([
30            'path' => $path,
31            'published' => $published,
32            'deleted' => $deleted,
33        ]);
34    }
35
36    public function byUid(
37        string $uid,
38        ?bool $deleted = false,
39        ?bool $published = true,
40    ): ?Wrapper {
41        return $this->get([
42            'uid' => $uid,
43            'published' => $published,
44            'deleted' => $deleted,
45        ]);
46    }
47
48    public function byHandle(
49        string $handle,
50        ?bool $deleted = false,
51        ?bool $published = true,
52    ): ?Wrapper {
53        return $this->get([
54            'handle' => $handle,
55            'published' => $published,
56            'deleted' => $deleted,
57        ]);
58    }
59
60    /**
61     * The node as the editor sees it: the working copy where one exists,
62     * the live row otherwise. Published or not, never deleted.
63     */
64    public function working(string $uid): ?Wrapper
65    {
66        return $this->get([
67            'uid' => $uid,
68            'published' => null,
69            'deleted' => false,
70            'working' => true,
71        ]);
72    }
73
74    /** @param array<string, mixed> $params */
75    public function get(
76        array $params,
77    ): ?Wrapper {
78        $data = $this->context
79            ->db
80            ->nodes
81            ->find($params)
82            ->first();
83
84        if (!$data) {
85            return null;
86        }
87
88        $data['content'] = json_decode($data['content'], true);
89        $data['title'] = json_decode($data['title'], true);
90        $data['editor_data'] = json_decode($data['editor_data'], true);
91        $data['creator_data'] = json_decode($data['creator_data'], true);
92        $data['paths'] = json_decode($data['paths'], true);
93        $data = self::foldDraft($data);
94        $class = $this->context
95            ->container
96            ->tag(Bootstrap::NODE_TAG)
97            ->entry($data['type_handle'])
98            ->definition();
99
100        if ($this->types->isNode($class)) {
101            $node = $this->nodeFactory->create($class, $this->context, $this->cms, $data);
102
103            return $this->nodeFactory->proxy($node, $this->context, $this->cms);
104        }
105
106        throw new HttpBadRequest($this->context->request);
107    }
108
109    /** @return list<Wrapper> */
110    public function find(
111        string $query,
112    ): array {
113        return [];
114    }
115
116    /**
117     * Folds the joined draft columns of a `nodes/find` row into one `draft`
118     * entry (null without a working copy). When the working copy was
119     * requested, its handle and paths replace the row's live values the
120     * same way its content did.
121     *
122     * @param array<string, mixed> $data
123     * @return array<string, mixed>
124     */
125    public static function foldDraft(array $data): array
126    {
127        $hasDraft = (bool) ($data['has_draft'] ?? false);
128        $data['draft'] = $hasDraft
129            ? [
130                'created' => $data['draft_created'],
131                'changed' => $data['draft_changed'],
132                'editor' => (int) $data['draft_editor'],
133                'editorName' => $data['draft_editor_name'] ?? null,
134            ]
135            : null;
136        $settings = json_decode((string) ($data['draft_settings'] ?? ''), true);
137        unset(
138            $data['has_draft'],
139            $data['draft_created'],
140            $data['draft_changed'],
141            $data['draft_editor'],
142            $data['draft_editor_name'],
143            $data['draft_settings'],
144        );
145
146        if (!$hasDraft || !is_array($settings)) {
147            return $data;
148        }
149
150        if (array_key_exists('handle', $settings)) {
151            $data['handle'] = $settings['handle'];
152        }
153
154        if (is_array($settings['paths'] ?? null)) {
155            $data['paths'] = $settings['paths'];
156        }
157
158        return $data;
159    }
160}