From 1b02532b0d492842a62e9e5a5f993124b4fb4f4c Mon Sep 17 00:00:00 2001 From: Roy de Jong Date: Mon, 24 Jun 2024 20:39:04 +0200 Subject: [PATCH] docs: mapping, crud, queries, validation --- README.md | 11 ++- docs/CRUD.md | 79 +++++++++++++++++ docs/ObjectMapping.md | 110 ++++++++++++++++++++++++ docs/Queries.md | 194 ++++++++++++++++++++++++++++++++++++++++++ docs/Validation.md | 34 ++++++++ 5 files changed, 427 insertions(+), 1 deletion(-) create mode 100644 docs/CRUD.md create mode 100644 docs/ObjectMapping.md create mode 100644 docs/Queries.md diff --git a/README.md b/README.md index 56ac6fd..9987591 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,17 @@ echo "Created user #{$user->id}!"; ## Features +### πŸ—ΊοΈ [Object Mapping](./docs/ObjectMapping.md) +Define your models as pure PHP classes with typed properties. Use them like regular objects. + +### πŸ“¦ [Easy CRUD](./docs/CRUD.md) +Use intuitive object-oriented CRUD (create, read, update, and delete) operations on your models. + +### πŸ”Ž [Query Builder](./docs/Queries.md) +Use the query builder to quickly build and run more complex queries with prepared statements. + ### 🀝 [Relationships](./docs/Relationships.md) -Define relationships between your models and easily load them in an optimized way. +Set up relationships between your models and easily load them in an optimized way. ### βœ… [Validation](./docs/Validation.md) Add constraints to your model properties and validate them with user-friendly error messages. diff --git a/docs/CRUD.md b/docs/CRUD.md new file mode 100644 index 0000000..88c3ef5 --- /dev/null +++ b/docs/CRUD.md @@ -0,0 +1,79 @@ +# CRUD +**Once your [models are set up](./ObjectMapping.md), you can immediately use CRUD (create, update, delete) operations on them with.** + +## Creating records +Initialize a model instance, set its properties, and call `save()` to insert it into the database: + +```php +$car = new Car(); +$car->make = "Toyota"; +$car->model = "Corolla"; +$car->year = 2005; +$car->save(); // INSERT INTO cars [..] +``` + +## Fetching by ID +Load a record by its primary key: + +```php +$car = Car::find(123); // SELECT * FROM cars WHERE id = 123 +``` + +## Updating records +Modify a model instance and call `save()` to update it in the database: + +```php +$car = Car::find(123); +$car->year = 2006; +$car->save(); // UPDATE cars SET year = 2006 WHERE id = 123 +``` + +Calling `save()` will only update the properties that have changed. + +## Upserting records +Upsert allows you to update an existing record, or insert a new one if it doesn't exist: + +```php +$car = new Car(); +$car->make = "VW"; +$car->model = "Golf"; +$car->year = 2005; +$car->upsert(); // INSERT INTO cars [..] ON DUPLICATE KEY UPDATE [..] +``` + +Calling `upsert()` will insert or update *all* values (not just dirty ones). + +## Deleting records +Call `delete()` on a model instance to remove it from the database: + +```php +$car = Car::find(123); +$car->delete(); // DELETE FROM cars WHERE id = 123 +``` + +## Querying records +Use the `query()` method to build complex queries: + +```php +$matchingCars = Car::query() + ->where('make = ?', 'Toyota') + ->queryAllModels(); +``` + +See the [Queries](./Queries.md) documentation for more details on query building. + +## Fetch matching existing record +Use the `fetchExisting()` method to quickly fetch any model with identical values: + +```php +$car = new Car(); +$car->make = "Toyota"; +$car->model = "Corolla"; +$car->year = 2005; + +if ($existingCar = $car->fetchExisting()) { + echo "Found matching existing car with ID: " . $existingCar->id; +} +``` + +You can even use `tryBecomeExisting()` to assume the properties and ID of an existing record if one can be found. \ No newline at end of file diff --git a/docs/ObjectMapping.md b/docs/ObjectMapping.md new file mode 100644 index 0000000..cf8d43c --- /dev/null +++ b/docs/ObjectMapping.md @@ -0,0 +1,110 @@ +# Object Mapping +**Define your models as pure PHP classes with typed properties, and get instant [CRUD operations](./CRUD.md).** + +## Defining models +Each model is mapped to a database table. + +A model itself is a pure PHP class with typed properties that extends from the `Model` base class that Instarecord provides. + +```php +where('make = ?', 'Toyota') + ->queryAllModels(); +``` + +When querying from a model context, the query will default to the model's table name, and you can use convenience functions like `queryAllModels()` to map the results back to model instances. + +### Global queries +You can start a global query by calling `Instarecord::query()`. + +```php +$matchingCars = Instarecord::query() + ->from('cars') + ->where('make = ?', 'Toyota') + ->queryAllRows(); +``` + +When querying from a global context, you can use the `query()` function directly on the `Instarecord` class to build queries that don't belong to any specific model. + +## Query functions +Chain query functions using a fluent interface to build your query. + +### `->select()` +Switch to a `SELECT [..] FROM` statement and set the selection text. The selection text defaults to `*`. + +You can manually set the selection text, and use bound parameters if needed: +```php +->select('make, model, year, ? AS some_value', '123') +``` + +### `->count()` +Switch to a `SELECT COUNT([..]) FROM` statement and set the count statement. The count statement defaults to `*`. + +### `->insert()` +Switch to an `INSERT INTO` statement. + +### `->insertIgnore()` +Switch to an `INSERT IGNORE INTO` statement. + +### `->update()` +Switch to an `UPDATE` statement. + +### `->delete()` +Switch to a `DELETE FROM` statement. + +### `->from()` / `->into()` +Sets the target table name on the query. + +This is already set if you are using a query from a model context, but can always be overridden. + +### `->values()` +Sets the values to be inserted on an `INSERT` statement. + +Pass an associative array of column names and values: + +```php +->values([ + 'make' => 'VW', + 'model' => 'Golf', + 'year' => 2005 +]) +``` + +### `->set()` +Sets the values to be `SET` on an `UPDATE` statement. This can be used in two ways: + +1. Pass raw SQL text and use bound parameters: +```php +->set('make = ?, model = ?, year = ?', + 'VW', 'Golf', 2005) +``` +2. Pass a key-value array of column names and values: +```php +->set([ + 'make' => 'VW', + 'model' => 'Golf', + 'year' => 2005 +]) +``` + +### `->onDuplicateKeyUpdate()` +Adds an `ON DUPLICATE KEY UPDATE` component to the query statement. This is used for upserts (insert or update). + +Pass a key-value array of column names and values to update: +```php +->onDuplicateKeyUpdate([ + 'make' => 'VW', + 'model' => 'Golf', + 'year' => 2005 +]) +``` + +The second parameter is optional and specifies the primary key column name for use with `LAST_INSERT_ID()` to retrieve the inserted ID. + +### `->innerJoin()` +Adds an `INNER JOIN` to the query. May use bound parameters. + +### `->leftJoin()` +Adds a `LEFT JOIN` to the query. May use bound parameters. + +### `->rightJoin()` +Adds a `RIGHT JOIN` to the query. May use bound parameters. + +### `->where()` +Removes any previously set `WHERE` clauses and sets a new one. May use bound parameters. + +### `->andWhere()` +Adds an additional `WHERE` clause to the query. May use bound parameters. + +Groups multiple where blocks using `WHERE (x) AND (y) AND (z)` query syntax. + +### `->having()` +Removes any previously set `HAVING` clauses and sets a new one. May use bound parameters. + +### `->andHaving()` +Adds an additional `HAVING` clause to the query. May use bound parameters. + +Groups multiple having blocks using `HAVING (x) AND (y) AND (z)` query syntax. + +### `->orderBy()` +Sets the `ORDER BY` statement on the query. May use bound parameters. + +### `->groupBy()` +Sets the `GROUP BY` statement on the query. May use bound parameters. + +### `->limit()` +Applies a `LIMIT` to the statement. + +### `->offset()` +Applies an `OFFSET` to the statement. Typically used for pagination. + +### `->forUpdate()` +Applies a `FOR UPDATE` to the statement. Used to lock rows for update. + +Replaces any previously set locking mode. + +### `->lockInShareMode()` +Applies a `LOCK IN SHARE MODE` to the statement. Used to lock rows for read. + +Replaces any previously set locking mode. + +## Executing queries +A query can be executed in several ways, each returning the results in a different way. + +### `->execute()` +Executes the query and returns the amount of affected rows. + +### `->executeInsert()` +Executes the query and attempts to return the last inserted ID. + +### `->queryAllRows()` +Executes the query and returns all rows as an array of associative arrays. + +### `->querySingleRow()` +Applies a `LIMIT 1` to the query and returns the first row as an associative array. + +### `->querySingleValue()` +Applies a `LIMIT 1` to the query and returns the first column of the first row as a string. + +### `->querySingleValueArray()` +Executes the query, fetches only the first value from each row, and combines those into an array. + +### `->queryKeyValueArray()` +Executes the query, fetches the first column as the key and the second column as the value (`PDO::FETCH_KEY_PAIR`), and combines those into an associative array. + +### `->queryAllModels()` +*Only available in Model query.* + +Executes the query and returns all rows as an array of model instances. + +### `->queryAllModelsIndexed()` +*Only available in Model query.* + +Same as queryAllModels(), but indexes all options by their PK value. + +### `->querySingleModel()` +*Only available in Model query.* + +Uses `querySingleRow()` to fetch a single row, and returns it as a model instance. + + + diff --git a/docs/Validation.md b/docs/Validation.md index 0ebad85..89c42a2 100644 --- a/docs/Validation.md +++ b/docs/Validation.md @@ -85,3 +85,37 @@ Requires the property to be non-null, non-empty and at least `$minLength` charac Requires the property to be at most `$maxLength` characters long. > {name} can't be longer than {maxLength} characters. + +## Custom validators +You can easily build custom validator attributes by simply extending the `ValidationAttribute` class. + +```php +someParam) { + return ValidationResult::fail($this->customError ?? "{$name} must be equal to '{$this->someParam}'."); + } + + return ValidationResult::pass(); + } +} +``` + +`validate()` will be called with the friendly property name (if available), and the property's current value. + +The validator should return a result by either calling `ValidationResult::pass()` or `ValidationResult::fail("some error")`. + +