]> BookStack Code Mirror - bookstack/blob - app/Api/ApiEntityListFormatter.php
23fa8e6ea7249274759e5b4fa98cf49b27a50a8a
[bookstack] / app / Api / ApiEntityListFormatter.php
1 <?php
2
3 namespace BookStack\Api;
4
5 use BookStack\Entities\Models\Entity;
6 use BookStack\Entities\Models\Page;
7
8 class ApiEntityListFormatter
9 {
10     /**
11      * The list to be formatted.
12      * @var Entity[]
13      */
14     protected array $list = [];
15
16     /**
17      * Whether to include related titles in the response.
18      */
19     protected bool $includeRelatedTitles = false;
20
21     /**
22      * The fields to show in the formatted data.
23      * Can be a plain string array item for a direct model field (If existing on model).
24      * If the key is a string, with a callable value, the return value of the callable
25      * will be used for the resultant value. A null return value will omit the property.
26      * @var array<string|int, string|callable>
27      */
28     protected array $fields = [
29         'id',
30         'name',
31         'slug',
32         'book_id',
33         'chapter_id',
34         'draft',
35         'template',
36         'priority',
37         'created_at',
38         'updated_at',
39     ];
40
41     public function __construct(array $list)
42     {
43         $this->list = $list;
44
45         // Default dynamic fields
46         $this->withField('url', fn(Entity $entity) => $entity->getUrl());
47     }
48
49     /**
50      * Add a field to be used in the formatter, with the property using the given
51      * name and value being the return type of the given callback.
52      */
53     public function withField(string $property, callable $callback): self
54     {
55         $this->fields[$property] = $callback;
56         return $this;
57     }
58
59     /**
60      * Show the 'type' property in the response reflecting the entity type.
61      * EG: page, chapter, bookshelf, book
62      * To be included in results with non-pre-determined types.
63      */
64     public function withType(): self
65     {
66         $this->withField('type', fn(Entity $entity) => $entity->getType());
67         return $this;
68     }
69
70     /**
71      * Include tags in the formatted data.
72      */
73     public function withTags(): self
74     {
75         $this->withField('tags', fn(Entity $entity) => $entity->tags);
76         return $this;
77     }
78
79     /**
80      * Enable the inclusion of related book and chapter titles in the response.
81      */
82     public function withRelatedTitles(): self
83     {
84         $this->includeRelatedTitles = true;
85
86         $this->withField('book_title', function (Entity $entity) {
87             if (method_exists($entity, 'book')) {
88                 return $entity->book?->name;
89             }
90             return null;
91         });
92
93         $this->withField('chapter_title', function (Entity $entity) {
94             if ($entity instanceof Page && $entity->chapter_id) {
95                 return optional($entity->getAttribute('chapter'))->name;
96             }
97             return null;
98         });
99
100         return $this;
101     }
102
103     /**
104      * Format the data and return an array of formatted content.
105      * @return array[]
106      */
107     public function format(): array
108     {
109         $results = [];
110
111         foreach ($this->list as $item) {
112             $results[] = $this->formatSingle($item);
113         }
114
115         return $results;
116     }
117
118     /**
119      * Format a single entity item to a plain array.
120      */
121     protected function formatSingle(Entity $entity): array
122     {
123         $result = [];
124         $values = (clone $entity)->toArray();
125
126         foreach ($this->fields as $field => $callback) {
127             if (is_string($callback)) {
128                 $field = $callback;
129                 if (!isset($values[$field])) {
130                     continue;
131                 }
132                 $value = $values[$field];
133             } else {
134                 $value = $callback($entity);
135                 if (is_null($value)) {
136                     continue;
137                 }
138             }
139
140             $result[$field] = $value;
141         }
142
143         return $result;
144     }
145 }