|
| 1 | +# Darn Tidy Object (DTO) |
1 | 2 |
|
2 |
| -# MaplePHP - Data Transfer Object (DTO) |
| 3 | +DTO stands for **Darn Tidy Object**, a playful twist on the traditional Data Transfer Object. But this isn’t your average DTO. It’s a fully-loaded toolkit for **traversing, transforming, and tidying up structured data** in PHP with style, power, and simplicity. |
3 | 4 |
|
4 |
| -The MaplePHP DTO library simplifies working with structured data in PHP by wrapping it into objects. This allows easy traversal and transformation through a chainable interface, ensuring consistent, safe data handling and reducing the risk of direct data manipulation. |
5 | 5 |
|
6 |
| -- **Encapsulation**: Encapsulates data within objects. |
7 |
| -- **Immutability**: Ensures data integrity by preventing accidental modification. |
8 |
| -- **Validation**: Facilitates data validation, ensuring the data conforms to specific types or formats. |
9 |
| -- **Data Transformation**: Easily modify and format data using built-in methods. |
10 |
| -- **Low Coupling**: Promotes separation of concerns, reducing dependencies between different parts of your code. |
11 |
| -- **Improved Readability**: Makes code cleaner and easier to understand. |
12 |
| -- **Simplified Testing**: DTOs are easier to test, as they contain only data and transformation logic. |
| 6 | +## 📦 Installation |
13 | 7 |
|
14 |
| ---- |
15 |
| - |
16 |
| -**Note:** MaplePHP DTO also includes polyfill classes for Multibyte String and Iconv support. |
17 |
| - |
18 |
| -## **1. Creating a DTO Object** |
19 |
| - |
20 |
| -The simplest way to start using **MaplePHP DTO** is with the `Traverse` class: |
21 |
| - |
22 |
| -```php |
23 |
| -use MaplePHP\DTO\Traverse; |
24 |
| - |
25 |
| -$obj = Traverse::value([ |
26 |
| - "firstname" => "<em>daniel</em>", |
27 |
| - "lastname" => "doe", |
28 |
| - "price" => "1999.99", |
29 |
| - "date" => "2023-08-21 14:35:12", |
30 |
| - "feed" => [ |
31 |
| - "t1" => ["firstname" => "<em>john 1</em>", "lastname" => "doe-1", 'salary' => 40000], |
32 |
| - "t2" => ["firstname" => "<em>jane 2</em>", "lastname" => "doe-2", 'salary' => 20000] |
33 |
| - ] |
34 |
| -]); |
| 8 | +```bash |
| 9 | +composer require maplephp/dto |
35 | 10 | ```
|
36 | 11 |
|
37 |
| -Now, `$obj` behaves like an object where you can access its properties directly. |
| 12 | +## 📦 Installation |
38 | 13 |
|
39 |
| ---- |
40 |
| - |
41 |
| -## **2. Accessing Data** |
42 |
| - |
43 |
| -### **Direct Property Access** |
44 |
| - |
45 |
| -```php |
46 |
| -echo $obj->firstname; |
47 |
| -// Output: <em>daniel</em> |
| 14 | +```bash |
| 15 | +composer require maplephp/dto |
48 | 16 | ```
|
49 | 17 |
|
50 |
| -### **Safe Fallback for Missing Values** |
| 18 | +## 📘 Documentation |
| 19 | +- [Why DTO?](http://localhost:3000/docs/intro#why-dto) |
| 20 | +- [Traverse Collection](http://localhost:3000/docs/traverse) |
| 21 | +- [Format string](http://localhost:3000/docs/format-string) |
| 22 | +- [Format Number](http://localhost:3000/docs/format-number) |
| 23 | +- [Format Clock](http://localhost:3000/docs/format-clock) |
| 24 | +- [Format Dom](http://localhost:3000/docs/format-dom) |
51 | 25 |
|
52 |
| -```php |
53 |
| -echo $obj->feed->t1->doNotExist->fallback('lorem')->strUcFirst(); |
54 |
| -// Output: Lorem |
55 |
| -``` |
56 | 26 |
|
57 |
| ---- |
| 27 | +## How It Works |
58 | 28 |
|
59 |
| -## **3. Working with Collections** |
| 29 | +DTO wraps your data arrays into a powerful, fluent object structure. Instead of cluttered array access, your code becomes expressive and self-documenting. |
60 | 30 |
|
61 |
| -### **Iterating Over Arrays** |
| 31 | +### Before DTO |
62 | 32 |
|
63 | 33 | ```php
|
64 |
| -foreach ($obj->feed->fetch() as $row) { |
65 |
| - echo $row->firstname->strStripTags()->strUcFirst(); |
66 |
| -} |
67 |
| -// Output: |
68 |
| -// John 1 |
69 |
| -// Jane 2 |
| 34 | +$name = isset($data['user']['profile']['name']) |
| 35 | + ? ucfirst(strip_tags($data['user']['profile']['name'])) |
| 36 | + : 'Guest'; |
70 | 37 | ```
|
71 | 38 |
|
72 |
| -### **Filtering Data (`filter`)** |
73 |
| - |
74 |
| -Filters an array based on a callback function. |
| 39 | +### With DTO |
75 | 40 |
|
76 | 41 | ```php
|
77 |
| -$filtered = $obj->feed->filter(fn($row) => $row->salary->get() > 30000); |
78 |
| -echo $filtered->count(); |
79 |
| -// Output: 1 |
| 42 | +$name = $obj->user->profile->name |
| 43 | + ->strStripTags() |
| 44 | + ->strUcFirst() |
| 45 | + ->fallback('Guest') |
| 46 | + ->get(); |
80 | 47 | ```
|
81 | 48 |
|
82 |
| -### **Finding Specific Values** |
83 |
| - |
84 |
| -```php |
85 |
| -echo $obj->shopList->search('cheese'); |
86 |
| -// Output: 3 |
87 |
| -``` |
88 |
| - |
89 |
| -```php |
90 |
| -echo $obj->feed->pluck('lastname')->toArray()[1]; |
91 |
| -// Output: doe-2 |
92 |
| -``` |
| 49 | +Much tidier, right? |
93 | 50 |
|
94 | 51 | ---
|
95 | 52 |
|
96 |
| -## **4. Transforming Collections** |
97 |
| - |
98 |
| -### **Mapping (`map`)** |
| 53 | +## ✨ Core Features |
99 | 54 |
|
100 |
| -Applies a function to each element. |
| 55 | +### Smart Data Traversal |
101 | 56 |
|
102 |
| -```php |
103 |
| -$mapped = $obj->shopList->map(fn($item) => strtoupper($item)); |
104 |
| -print_r($mapped->toArray()); |
105 |
| -``` |
106 |
| -**Output:** |
107 |
| -```php |
108 |
| -['SOAP', 'TOOTHBRUSH', 'MILK', 'CHEESE', 'POTATOES', 'BEEF', 'FISH'] |
109 |
| -``` |
110 |
| - |
111 |
| -### **Reducing (`reduce`)** |
112 |
| - |
113 |
| -Combines values into a single result. |
| 57 | +Access deeply nested data without ever worrying about undefined keys. |
114 | 58 |
|
115 | 59 | ```php
|
116 |
| -$sum = $obj->feed->reduce(fn($carry, $item) => $carry + $item->salary->get(), 0); |
117 |
| -echo $sum; |
118 |
| -// Output: 60000 |
119 |
| -``` |
120 |
| - |
121 |
| -### **Sorting (`reverse`, `shuffle`)** |
122 |
| - |
123 |
| -```php |
124 |
| -echo $obj->shopList->reverse()->eq(0); |
125 |
| -// Output: fish |
126 |
| -``` |
| 60 | +echo $obj->article->tagline->strToUpper(); |
| 61 | +// Result: 'HELLO WORLD' |
127 | 62 |
|
128 |
| -```php |
129 |
| -echo $obj->shopList->shuffle()->eq(0); // Random Output |
130 |
| -``` |
131 |
| - |
132 |
| -### **Chunking and Slicing (`chunk`, `slice`, `splice`)** |
133 |
| - |
134 |
| -```php |
135 |
| -echo $obj->shopList->chunk(3)->count(); |
136 |
| -// Output: 3 |
137 |
| -``` |
138 |
| - |
139 |
| -```php |
140 |
| -echo $obj->shopList->slice(1, 2)->count(); |
141 |
| -// Output: 2 |
142 |
| -``` |
143 |
| - |
144 |
| -```php |
145 |
| -$spliced = $obj->shopList->splice(1, 2, ['replaced'])->toArray(); |
146 |
| -print_r($spliced); |
147 |
| -``` |
148 |
| -**Output:** |
149 |
| -```php |
150 |
| -['soap', 'replaced', 'potatoes', 'beef', 'fish'] |
| 63 | +echo $obj->article->content->strExcerpt()->strUcFirst(); |
| 64 | +// Result: 'Lorem ipsum dolor sit amet...' |
151 | 65 | ```
|
152 | 66 |
|
153 | 67 | ---
|
154 | 68 |
|
155 |
| -## **5. Modifying Collections** |
156 |
| - |
157 |
| -### **Adding and Removing Items** |
| 69 | +### Built-In Data Transformation |
158 | 70 |
|
159 |
| -```php |
160 |
| -echo $obj->shopList->push('barbie')->count(); |
161 |
| -// Output: 8 |
162 |
| -``` |
| 71 | +Transform values directly using built-in helpers like: |
163 | 72 |
|
164 |
| -```php |
165 |
| -echo $obj->shopList->pop($value)->count(); |
166 |
| -echo $value; |
167 |
| -// Output: fish |
168 |
| -``` |
| 73 | +#### Strings (`str`) |
169 | 74 |
|
170 | 75 | ```php
|
171 |
| -echo $obj->shopList->shift($value)->count(); |
172 |
| -echo $value; |
173 |
| -// Output: soap |
| 76 | +echo $obj->title->strSlug(); |
| 77 | +// Result: 'my-awesome-title' |
174 | 78 | ```
|
175 | 79 |
|
176 |
| ---- |
177 |
| - |
178 |
| -## **6. Advanced Traversal & Recursion** |
179 |
| - |
180 |
| -### **Walking Through Nested Structures (`walk`, `walkRecursive`)** |
| 80 | +#### Numbers (`num`) |
181 | 81 |
|
182 | 82 | ```php
|
183 |
| -$value = ""; |
184 |
| -$obj->feed->walkRecursive(function ($val) use (&$value) { |
185 |
| - $value .= strip_tags(str_replace(" ", "", $val)); |
186 |
| -}); |
187 |
| -echo $value; |
188 |
| -// Output: john1doe-1400001jane2doe-2200002 |
189 |
| -``` |
190 |
| - |
191 |
| -### **Flattening Data (`flatten`, `flattenWithKeys`)** |
| 83 | +echo $obj->filesize->numToFilesize(); |
| 84 | +// Result: '1.95 kb' |
192 | 85 |
|
193 |
| -```php |
194 |
| -$flatten = $obj->feed->flatten()->map(fn($row) => $row->strToUpper())->toArray(); |
| 86 | +echo $obj->price->numRound(2)->numToCurrency("USD"); |
| 87 | +// Result: $1,999.99 |
195 | 88 | ```
|
196 | 89 |
|
197 |
| ---- |
198 |
| - |
199 |
| -## **7. String, Number, and Date Handling** |
200 |
| - |
201 |
| -### **String Manipulations** |
| 90 | +#### Dates (`clock`) |
202 | 91 |
|
203 | 92 | ```php
|
204 |
| -echo $obj->firstname->strStripTags()->strUcFirst(); |
205 |
| -// Output: Daniel |
206 |
| -``` |
207 |
| - |
208 |
| -### **Number Formatting** |
| 93 | +echo $obj->created_at->clockFormat('d M, Y', 'sv_SE'); |
| 94 | +// Result: '21 augusti 2025' |
209 | 95 |
|
210 |
| -```php |
211 |
| -echo $obj->price->numToFilesize(); |
212 |
| -// Output: 1.95 kb |
| 96 | +echo $obj->created_at->clockIsToday(); |
| 97 | +// Result: true |
213 | 98 | ```
|
214 | 99 |
|
215 |
| -```php |
216 |
| -echo $obj->price->numRound(2)->numCurrency("SEK", 2); |
217 |
| -// Output: 1 999,99 kr |
218 |
| -``` |
219 |
| - |
220 |
| -### **Date Handling** |
| 100 | +#### HTML DOM Builder (`dom`) |
221 | 101 |
|
222 | 102 | ```php
|
223 |
| -echo $obj->date->clockFormat("y/m/d, H:i"); |
224 |
| -// Output: 23/08/21, 14:35 |
| 103 | +echo $obj->heading->domTag("h1.title"); |
| 104 | +// Result: <h1 class="title">My Heading</h1> |
225 | 105 | ```
|
226 | 106 |
|
| 107 | +Or nest elements with ease: |
| 108 | + |
227 | 109 | ```php
|
228 |
| -\MaplePHP\DTO\Format\Clock::setDefaultLanguage('sv_SE'); |
229 |
| -echo $obj->date->clockFormat('d M'); |
230 |
| -// Output: 21 augusti |
| 110 | +echo $obj->title->domTag("h1.title")->domTag("header"); |
| 111 | +// Result: <header><h1 class="title">Hello</h1></header> |
231 | 112 | ```
|
232 | 113 |
|
233 | 114 | ---
|
234 | 115 |
|
235 |
| -## **8. Array Utility Methods** |
| 116 | +### Built-In Collection Support |
236 | 117 |
|
237 |
| -### **Merging and Replacing Arrays** |
| 118 | +Work with arrays of objects just as cleanly: |
238 | 119 |
|
239 | 120 | ```php
|
240 |
| -$merged = $obj->shopList->merge(['eggs', 'bread']); |
241 |
| -print_r($merged->toArray()); |
242 |
| -``` |
243 |
| -**Output:** |
244 |
| -```php |
245 |
| -['soap', 'toothbrush', 'milk', 'cheese', 'potatoes', 'beef', 'fish', 'eggs', 'bread'] |
| 121 | +foreach ($obj->users->fetch() as $user) { |
| 122 | + echo $user->firstName->strUcFirst(); |
| 123 | +} |
246 | 124 | ```
|
247 | 125 |
|
248 |
| -```php |
249 |
| -$replaced = $obj->shopList->replaceRecursive([0 => 'soap_bar']); |
250 |
| -print_r($replaced->toArray()); |
251 |
| -``` |
252 |
| -**Output:** |
253 |
| -```php |
254 |
| -['soap_bar', 'toothbrush', 'milk', 'cheese', 'potatoes', 'beef', 'fish'] |
255 |
| -``` |
| 126 | +--- |
256 | 127 |
|
257 |
| -### **Computing Differences (`diff`, `diffAssoc`, `diffKey`)** |
| 128 | +### Modify Data on the Fly |
258 | 129 |
|
259 |
| -```php |
260 |
| -$diff = $obj->shopList->diff(['milk', 'cheese']); |
261 |
| -print_r($diff->toArray()); |
262 |
| -``` |
263 |
| -**Output:** |
264 |
| -```php |
265 |
| -['soap', 'toothbrush', 'potatoes', 'beef', 'fish'] |
266 |
| -``` |
| 130 | +Change values directly without verbose conditionals: |
267 | 131 |
|
268 | 132 | ```php
|
269 |
| -$diffAssoc = $obj->shopList->diffAssoc(['soap', 'toothbrush']); |
270 |
| -print_r($diffAssoc->toArray()); |
| 133 | +$updated = $obj->shoppingList->replace([0 => 'Shampoo']); |
| 134 | +print_r($updated->toArray()); |
271 | 135 | ```
|
272 |
| -**Output:** |
273 |
| -```php |
274 |
| -['milk', 'cheese', 'potatoes', 'beef', 'fish'] |
275 |
| -``` |
276 |
| - |
277 |
| -### **Extracting Keys (`keys`, `pluck`)** |
278 | 136 |
|
279 |
| -```php |
280 |
| -print_r($obj->shopList->keys()->toArray()); |
281 |
| -``` |
282 |
| -**Output:** |
283 |
| -```php |
284 |
| -[0, 1, 2, 3, 4, 5, 6] |
285 |
| -``` |
| 137 | +--- |
286 | 138 |
|
287 |
| -```php |
288 |
| -print_r($obj->feed->pluck('lastname')->toArray()); |
289 |
| -``` |
290 |
| -**Output:** |
291 |
| -```php |
292 |
| -['doe-1', 'doe-2'] |
293 |
| -``` |
| 139 | +Now go forth, write cleaner code, and let DTO handle the messy parts. |
0 commit comments