You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
5.8 KiB
162 lines
5.8 KiB
# Nesting Models
|
|
|
|
## Objectives
|
|
|
|
Currently, we only have two unrelated models: people and locations. Let's add the functionality so that whenever we have JSON that represents a person, it will contain a `home` property, which is the associated `location` object:
|
|
|
|
```JSON
|
|
{
|
|
"id":4,
|
|
"name":"Bob",
|
|
"age":25,
|
|
"home": {
|
|
"id":2,
|
|
"street": "123 Fake Street",
|
|
"city": "Aurora",
|
|
"state": "NY"
|
|
}
|
|
}
|
|
```
|
|
|
|
Also, when we have a location, it will contain an array of people objects:
|
|
|
|
```JSON
|
|
{
|
|
"id":2,
|
|
"street": "123 Fake Street",
|
|
"city": "Aurora",
|
|
"state": "NY",
|
|
"inhabitants":[
|
|
{
|
|
"id":4,
|
|
"name":"Bob",
|
|
"age":25
|
|
},
|
|
{
|
|
"id":7,
|
|
"name":"Sally",
|
|
"age":74
|
|
},
|
|
]
|
|
}
|
|
```
|
|
|
|
This will be a one-to-many relationship
|
|
|
|
## Add home_id to people
|
|
|
|
First, go into `psql` and add a `home_id` INT column to the `people` table:
|
|
|
|
```sql
|
|
ALTER TABLE people ADD COLUMN home_id INT;
|
|
```
|
|
|
|
Now we can give people homes like so:
|
|
|
|
```sql
|
|
UPDATE people SET home_id = 2 WHERE id = 4;
|
|
```
|
|
|
|
The previous code will find the row with an `id` of 4 and set the `home_id` column to 2.
|
|
|
|
## Update People SQL
|
|
|
|
The first thing we want to do is to update the SQL code in the `People` class so that each row from the `people` table is joined onto a corresponding row from the `locations` table. Once we have the additional columns for each row in the `people` table, we can use that information to create a home for each `Person` object. Try the following in `psql`:
|
|
|
|
```SQL
|
|
SELECT * FROM people JOIN locations ON people.home_id = locations.id;
|
|
```
|
|
|
|
The above SQL statement will only show rows where the people have a value in the `home_id` column. This is because Postgres is attempting to create rows by matching rows in the `people` table with rows in the `locations` table, based on whether `people.home_id = locations.id`. When it encounters a row from the `people` table where the `home_id` column is `NULL`, it can't find any rows from the `locations` table to match it with, since no rows from `locations` have an `id` column of `NULL`. Since no matches can be found, it doesn't include that row from `people`. We need to perform the join and then add any missing rows from `people`:
|
|
|
|
```SQL
|
|
SELECT * FROM people LEFT JOIN locations ON people.home_id = locations.id;
|
|
```
|
|
|
|
Now that we have all the people rows, we're ready to plug that into our php. Alter the `pg_query` code in our `People::all()` function:
|
|
|
|
```php
|
|
$results = pg_query("SELECT * FROM people LEFT JOIN locations ON people.home_id = locations.id");
|
|
```
|
|
|
|
If you view http://localhost:8888/people, you'll see something funky. Some people have `NULL` ids, and other rows have unexpected `id` columns. The reason for this can be discovered by looking back in `psql` at our previous `LEFT JOIN` statement results. You'll notice there are two `id` columns, one for the `people` table and one for `locations` table. When PHP attempts to convert a row into an object, when it reads the `people` table's `id` column, creates an `id` property for the object. It then goes through, adding `name`, `age`, and `home_id` properties. It then reaches the `id` column for the `locations` table and overwrites the `id` property on the `$row_object` with the value from the `id` column of the `locations` table.
|
|
|
|
To fix this, we can alter our SQL statement to rename one or both of the id columns. Let's rename the `id` column from the `locations` table.
|
|
|
|
```SQL
|
|
SELECT
|
|
people.*,
|
|
locations.id AS location_id,
|
|
locations.street,
|
|
locations.city,
|
|
locations.state
|
|
FROM people
|
|
LEFT JOIN locations
|
|
ON people.home_id = locations.id;
|
|
```
|
|
|
|
Now if we change our SQL statement in PHP, we'll see better results:
|
|
|
|
```php
|
|
$results = pg_query("SELECT
|
|
people.*,
|
|
locations.id AS location_id,
|
|
locations.street,
|
|
locations.city,
|
|
locations.state
|
|
FROM people
|
|
LEFT JOIN locations
|
|
ON people.home_id = locations.id;");
|
|
```
|
|
|
|
## Give People Locations
|
|
|
|
If we put a `var_dump` inside the while loop in our `People::all()` function, we can see `$row_object` now contains information about each person's location (if you're using Postman to view http://localhost:8888/people, you might need to switch to the `Raw` view).
|
|
|
|
```php
|
|
while($row_object){
|
|
var_dump($row_object); //print $row_object so we can see what it looks like now
|
|
|
|
$new_person = new Person(
|
|
$row_object->id,
|
|
$row_object->name,
|
|
$row_object->age
|
|
);
|
|
$people[] = $new_person;
|
|
|
|
$row_object = pg_fetch_object($results);
|
|
}
|
|
```
|
|
|
|
Now lets use the additional information on `$row_object` to create Location objects and add them to `$new_person` where necessary. First, let's include the `Location` model at the top of `models/person.php`.
|
|
|
|
```php
|
|
include_once __DIR__ . '/location.php';
|
|
```
|
|
|
|
Now, inside the `People::all()` `while` loop, we'll add the logic to create a location and add it to `$new_person`:
|
|
|
|
```php
|
|
$new_person = new Person(
|
|
$row_object->id,
|
|
$row_object->name,
|
|
$row_object->age
|
|
);
|
|
|
|
if($row_object->location_id){ //test if location_id is truthy
|
|
$new_location = new Location( //create a location from the row data
|
|
intval($row_object->location_id), //turn the string into an int
|
|
$row_object->street,
|
|
$row_object->city,
|
|
$row_object->state
|
|
);
|
|
$new_person->home = $new_location; //attach $new_location to $new_person->home
|
|
}
|
|
|
|
$people[] = $new_person;
|
|
|
|
$row_object = pg_fetch_object($results);
|
|
```
|
|
|
|
Here we test to see if `$row_object->location_id` is truthy. If it has a value -- a string representation of the id column of `locations` table -- then the block of code belonging to the `if` statement will run, creating `$new_location` and attaching it to the `home` property of `$new_person`.
|