|
| 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