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.7 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),
$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`.