Uczymy się relacji jeden do jednego w Symfony. Kontynuacja lekcji poprzednich, zaleca się dobre przerobienie materiału. Do dzieła.
Ok, musimy utworzyć entity o nazwie Person:
- name, string, 255, nullable – nie
- age, integer, nullable – nie
Teraz entity o nazwie PersonAddress:
- city, string, 255, nullable – nie
- street, string, 255, nullable – nie
- person:
- typ – OneToOne
- klasa – Person
- nullable – nie
- dwukierunkowe – tak
- nazwa – personAddress
Na tym poziomie zakładam, że umiemy korzystać z makera, robić entity, migracje, migrować. Teraz tworzymy PersonController, jak już mamy sprawdzone, że utworzyło nam w bazie danych tabele.
A właśnie – jak one wyglądają? Person :
# Nazwa Typ Collation Attributes Null Default Comments Extra
1 id Podstawowy int(11) Nie Brak AUTO_INCREMENT
2 name varchar(255) utf8mb4_unicode_ci Nie Brak
3 age int(11) Nie Brak
A teraz PersonAddress:
# Nazwa Typ Collation Attributes Null Default Comments Extra
1 id Podstawowy int(11) Nie Brak AUTO_INCREMENT
2 person_id Indeks int(11) Nie Brak
3 city varchar(255) utf8mb4_unicode_ci Nie Brak
4 street varchar(255) utf8mb4_unicode_ci Nie Brak
Ok, tworzymy PersonController i dokonujemy importów:
use App\Entity\Person;
use App\Entity\PersonAddress;
use App\Repository\PersonRepository;
use Doctrine\ORM\EntityManagerInterface;
Tworzymy testowy route, który ma nam utworzyć i zapisać dane z relacją:
#[Route('/person_test_route', name: 'app_person_test_route')]
public function testRoute(EntityManagerInterface $entityManager): Response
{
$person = new Person();
$person->setName("Jane Doe");
$person->setAge(20);
$addr = new PersonAddress();
$addr->setCity("London");
$addr->setStreet("London Street");
$addr->setPerson($person);
$entityManager->persist($person);
$entityManager->persist($addr);
$entityManager->flush();
return $this->render('person/index.html.twig', [
'controller_name' => 'PersonController',
]);
}
Teraz sprawdzamy, czy mamy w bazie danych. Za pierwszym razem nie zrobiłem persist na addr, żeby mieć celowo jeden obiekt bez adresu (John Doe, potem zamieniłem na Jane i dałem persist na adresie).
Zaraz wyjaśnię dlaczego, na razie zmienimy indeks:
#[Route('/person', name: 'app_person')]
public function index(PersonRepository $repository): Response
{
return $this->render('person/index.html.twig', [
'controller_name' => 'PersonController',
'people' => $repository->findAll()
]);
}
Ok, zmieniamy index.html.twig:
{% extends 'base.html.twig' %}
{% block title %}Hello PersonController!{% endblock %}
{% block body %}
<style>
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
</style>
<div class="example-wrapper">
<h1>Hello {{ controller_name }}! ✅</h1>
{% if people|length > 0 %}
{% for person in people %}
<h3><a href="#">Name: {{ person.name }}<a/></h3>
<p>Age: {{person.age}}</p>
{% if person.personAddress %}
<p>City: {{person.personAddress.city}}</p>
<p>Street:{{person.personAddress.street}}</p>
{% else %}
<p>Address: unknown</p>
{% endif %}
{% endfor %}
{% else %}
<p>No people</p>
{% endif %}
</div>
{% endblock %}
Mamy tutaj obsługę sytuacji, w której jeden rekord (John Doe) nie ma adresu i takiej, w której adres istnieje i jest wyświetlany.
Jak widać proste relacje jeden do jednego są bez ceregieli zaciągane od razu (co nie zawsze dotyczy relacji, w których jest ArrayCollection, czyli wiele rekordów, które nie zawsze chcemy dociągać).