Skip to content

Commit 76b58e4

Browse files
committed
Updating docs
1 parent a04aeb0 commit 76b58e4

File tree

1 file changed

+285
-3
lines changed

1 file changed

+285
-3
lines changed

README.md

Lines changed: 285 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,53 @@ class Breakfast_Model extends Model {
7878
}
7979
```
8080

81+
### A ReadOnly model
82+
83+
This is a model whose intent is to only read and store data. The Read operations should - in most cases - be deferred to
84+
a repository class, but the model should provide a simple interface for interacting with the repository. You can create
85+
ReadOnly model by implementing the `Contracts\ModelReadOnly` contract.
86+
87+
```php
88+
namespace Boomshakalaka\Whatever;
89+
90+
use Boomshakalaka\StellarWP\Models\Contracts;
91+
use Boomshakalaka\StellarWP\Models\Model;
92+
use Boomshakalaka\StellarWP\Models\ModelQueryBuilder;
93+
94+
class Breakfast_Model extends Model implements Contracts\ModelReadOnly {
95+
/**
96+
* @inheritDoc
97+
*/
98+
protected $properties = [
99+
'id' => 'int',
100+
'name' => 'string',
101+
'price' => 'float',
102+
'num_eggs' => 'int',
103+
'has_bacon' => 'bool',
104+
];
105+
106+
/**
107+
* @inheritDoc
108+
*/
109+
public static function find( $id ) : Model {
110+
return App::get( Repository::class )->get_by_id( $id );
111+
}
112+
113+
/**
114+
* @inheritDoc
115+
*/
116+
public static function query() : ModelQueryBuilder {
117+
return App::get( Repository::class )->prepare_query();
118+
}
119+
}
120+
```
121+
81122
### A CRUD model
82123

124+
This is a model that includes CRUD operations. Ideally, the actual CRUD operations should be deferred to and handled by
125+
a repository class, but the model should provide a simple interface for interacting with the repository. We get a CRUD
126+
model by implementing the `Contracts\ModelCrud` contract.
127+
83128
```php
84129
namespace Boomshakalaka\Whatever;
85130

@@ -133,37 +178,274 @@ class Breakfast_Model extends Model implements Contracts\ModelCrud {
133178
* @inheritDoc
134179
*/
135180
public static function query() : ModelQueryBuilder {
136-
return App::get( Repository::class )->prepare_query();
181+
return App::get( Repository::class )->prepareQuery();
137182
}
138183
}
139184
```
140185

141-
## Interacting with a model
186+
## Data Transfer Objects
142187

143-
## Data transfer objects
188+
Data Transfer Objects (DTOs) are classes that help with the translation of database query results (or other sources of data)
189+
into models. DTOs are not required for using this library, but they are recommended. Using these objects helps you be more
190+
deliberate with your query usage and allows your models and repositories well with the `ModelQueryBuilder`.
191+
192+
Here's an example of a DTO for breakfasts:
193+
194+
```php
195+
namespace Boomshakalaka\Whatever;
196+
197+
use Boomshakalaka\Whatever\StellarWP\Models\DataTransferObject;
198+
use Boomshakalaka\Whatever\Breakfast_Model;
199+
200+
class Breakfast_DTO extends DataTransferObject {
201+
/**
202+
* Breakfast ID.
203+
*
204+
* @var int
205+
*/
206+
public int $id;
207+
208+
/**
209+
* Breakfast name.
210+
*
211+
* @var string
212+
*/
213+
public string $name;
214+
215+
/**
216+
* Breakfast price.
217+
*
218+
* @var float
219+
*/
220+
public float $price;
221+
222+
/**
223+
* Number of eggs in the breakfast.
224+
*
225+
* @var int
226+
*/
227+
public int $num_eggs;
228+
229+
/**
230+
* Whether or not the breakfast has bacon.
231+
*
232+
* @var bool
233+
*/
234+
public bool $has_bacon;
235+
236+
/**
237+
* Builds a new DTO from an object.
238+
*
239+
* @since TBD
240+
*
241+
* @param object $object The object to build the DTO from.
242+
*
243+
* @return Breakfast_DTO The DTO instance.
244+
*/
245+
public static function fromObject( $object ): self {
246+
$self = new self();
247+
248+
$self->id = $object->id;
249+
$self->name = $object->name;
250+
$self->price = $object->price;
251+
$self->num_eggs = $object->num_eggs;
252+
$self->has_bacon = (bool) $object->has_bacon;
253+
254+
return $self;
255+
}
256+
257+
/**
258+
* Builds a model instance from the DTO.
259+
*
260+
* @since TBD
261+
*
262+
* @return Breakfast_Model The model instance.
263+
*/
264+
public function toModel(): Breakfast_Model {
265+
$attributes = get_object_vars( $this );
266+
267+
return new Breakfast_Model( $attributes );
268+
}
269+
}
270+
```
271+
272+
## Repositories
273+
274+
Repositories are classes that fetch from and interact with the database. Ideally, repositories would be used to
275+
query the database in different ways and return corresponding models. With this library, we provide
276+
`Deletable`, `Insertable`, and `Updatable` contracts that can be used to indicate what operations a repository provides.
277+
278+
You may be wondering why there isn't a `Findable` or `Readable` contract (or similar). That's because the fetching needs
279+
of a repository varies with the usecase. However, in the `Repository` abstract class, there is an abstract `prepareQuery()`
280+
method. This method should return a `ModelQueryBuilder` instance that can be used to fetch data from the database.
281+
282+
```php
283+
namespace Boomshakalaka\Whatever;
284+
285+
use Boomshakalaka\StellarWP\Models\Contracts\Model;
286+
use Boomshakalaka\StellarWP\Models\ModelQueryBuilder;
287+
use Boomshakalaka\StellarWP\Repositories\Repository;
288+
use Boomshakalaka\StellarWP\Repositories\Contracts;
289+
use Boomshakalaka\Whatever\Breakfast_Model;
290+
use Boomshakalaka\Whatever\Breakfast as Table;
291+
292+
class Breakfast_Repository extends Repository implements Contracts\Deletable, Contracts\Insertable, Contracts\Updatable {
293+
/**
294+
* {@inheritDoc}
295+
*/
296+
public function delete( Model $model ): bool {
297+
return (bool) DB::delete( Table::table_name(), [ 'id' => $model->id ], [ '%d' ] );
298+
}
299+
300+
/**
301+
* {@inheritDoc}
302+
*/
303+
public function insert( Model $model ): Breakfast_Model {
304+
DB::insert( Table::table_name(), [
305+
'name' => $model->name,
306+
'price' => $model->price,
307+
'num_eggs' => $model->num_eggs,
308+
'has_bacon' => (int) $model->has_bacon,
309+
], [
310+
'%s',
311+
'%s',
312+
'%d',
313+
'%d',
314+
] );
315+
316+
$model->id = DB::last_insert_id();
317+
318+
return $model;
319+
}
320+
321+
/**
322+
* {@inheritDoc}
323+
*/
324+
function prepareQuery(): ModelQueryBuilder {
325+
$builder = new ModelQueryBuilder( Breakfast_Model::class );
326+
327+
return $builder->from( Table::table_name( false ) );
328+
}
329+
330+
/**
331+
* {@inheritDoc}
332+
*/
333+
public function update( Model $model ): Model {
334+
DB::update( Table::table_name(), [
335+
'name' => $model->name,
336+
'price' => $model->price,
337+
'num_eggs' => $model->num_eggs,
338+
'has_bacon' => (int) $model->has_bacon,
339+
], [ 'id' => $model->id ], [
340+
'%s',
341+
'%s',
342+
'%d',
343+
'%d',
344+
], [ '%d' ] );
345+
346+
return $model;
347+
}
348+
349+
/**
350+
* Finds a Breakfast by its ID.
351+
*
352+
* @since TBD
353+
*
354+
* @param int $id The ID of the Breakfast to find.
355+
*
356+
* @return Breakfast_Model|null The Breakfast model instance, or null if not found.
357+
*/
358+
public function find_by_id( int $id ): ?Breakfast_Model {
359+
return $this->prepareQuery()->where( 'id', $id )->get();
360+
}
361+
}
362+
```
363+
364+
### Interacting with the Repository
365+
366+
### Querying
367+
368+
```php
369+
$breakfast = App::get( Breakfast_Repository::class )->find_by_id( 1 );
370+
371+
// Or, we can fetch via the model, which defers to the repository.
372+
$breakfast = Breakfast_Model::find( 1 );
373+
```
374+
375+
### Inserting
376+
377+
```php
378+
$breakfast = new Breakfast_Model( [
379+
'name' => 'Bacon and Eggs',
380+
'price' => 5.99,
381+
'num_eggs' => 2,
382+
'has_bacon' => true,
383+
] );
384+
385+
$breakfast->save();
386+
```
387+
388+
### Updating
389+
390+
```php
391+
$breakfast = Breakfast_Model::find( 1 );
392+
$breakfast->setAttribute( 'price', 6.99 );
393+
$breakfast->save();
394+
```
395+
396+
### Deleting
397+
398+
```php
399+
$breakfast = Breakfast_Model::find( 1 );
400+
$breakfast->delete();
401+
```
144402

145403
## Classes of note
146404

147405
### `Model`
148406

407+
This is an abstract class to extend for your models.
408+
149409
### `ModelFactory`
150410

411+
This is an abstract class to extend for creating model factories.
412+
151413
### `ModelQueryBuilder`
152414

415+
This class extends the [`stellarwp/db`](https://github.com/stellarwp/db) `QueryBuilder` class so that it returns
416+
model instances rather than arrays or `stdClass` instances. Using this requires models that implement the `ModelFromQueryBuilderObject`
417+
interface.
418+
153419
### `DataTransferObject`
154420

421+
This is an abstract class to extend for your DTOs.
422+
155423
### `Repositories\Repository`
156424

425+
This is an abstract class to extend for your repositories.
426+
157427
## Contracts of note
158428

159429
### `Contracts\ModelCrud`
160430

431+
Provides definitions of methods for CRUD operations in a model.
432+
161433
### `Contracts\ModelHasFactory`
162434

435+
Provides definition for factory methods within a model.
436+
163437
### `Contracts\ModelReadOnly`
164438

439+
Provides method signatures for read operations in a model.
440+
165441
### `Repositories\Contracts\Deletable`
166442

443+
Provides method signatures for delete methods in a repository.
444+
167445
### `Repositories\Contracts\Insertable`
168446

447+
Provides method signatures for insert methods in a repository.
448+
169449
### `Repositories\Contracts\Updatable`
450+
451+
Provides method signatures for update methods in a repository.

0 commit comments

Comments
 (0)