Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [ORM] Jak prawidłowo powiązać encje?
Forum PHP.pl > Forum > PHP
WebCM
Istnieją następujące tabele w bazie:

FILMY
- ID
- tytul

AKTORZY
- ID
- film (klucz obcy do FILMY.ID)
- imie

Załóżmy następujące klasy:
  1. class Film
  2. {
  3. public $id;
  4. public $tytul;
  5. }
  6. class Aktor
  7. {
  8. public $id;
  9. public $film; //niektóre ORM używają __get(), więc musi być private/protected
  10. public $imie;
  11. }

Chcę napisać kod zgodny z przyjętymi zasadami i jednocześnie czytelny. Powstają pytania:

1. Jak nazwać kolumny, które są kluczami obcymi?

Najłatwiej nazwać kolumnę film, ale nie wszystkie ORM sobie z tym poradzą ze względu na konflikt nazw.

  1. <?php
  2. $wartosc = $aktor->film->tytul;
  3.  
  4. // jeśli pole $film jest publiczne, wystąpi błąd, bo $film zawiera ID filmu
  5. // jeśli pole $film jest prywatne, otrzymamy tytuł filmu, jeśli ORM sobie poradzi
  6. ?>

2. Czy do klasy encji powinien trafić także ID powiązanego rekordu?

  1. class Aktor extends Encja
  2. {
  3. public $ID;
  4. public $filmid; //zakładając, że nazwa kolumny w bazie to filmid
  5. public $imie;
  6. private $film; //pole w zasadzie zbędne, to tylko informacja dla IDE
  7.  
  8. //Niektóre ORM-y wymagają zdefiniowania powiązań między encjami
  9. public function inicjuj()
  10. {
  11. $this->nalezyDo('filmid', Film::class, 'ID'); //pole filmid odwołuje się do ID encji Film
  12. }
  13.  
  14. //A także trzeba podać nazwę tabeli
  15. public function nazwaTabeli() { return 'AKTORZY'; }
  16. }

Zamiast funkcji można użyć także adnotacji lub pliku konfiguracyjnego. Przykład wyżej ma pole $filmid, ale do relacji odwołujemy się poprzez metodę magiczną __get(). Czy to zbyt nie komplikuje kodu? Może większość bibliotek ORM od razu pobiera powiązane encje n:1. Czy w powyższym przypadku po odwołaniu do $filmid (lub $film z poprzednich przykładów) powinniśmy otrzymać ID filmu czy obiekt klasy Film?

PS. Co myślicie o potworkach typu Film::find()?
Crozin
1. Zacznijmy od tego, że filmy i aktorzy tworza relację wiele-do-wielu, wiele-do-jednego.
2. O jaki konkretny ORM chodzi?
WebCM
1. Masz napisane wiele-do-jednego (n:1).
2. O wszystkie - jak to jest w dobrze napisanych ORM - a konkretny przypadek to framework Phalcon.
Crozin
Miało być: [...] tworzą relację wiele-do-wielu, NIE wiele-do-jednego..

Żeby było dobrze powinno być:
  1. class Film {
  2. private $id; // id
  3.  
  4. private $title; // string
  5.  
  6. private $actors; // array[Actor], czy jakaś obiektowa kolekcja, np. ArrayColection[Actor] z Doctrine
  7. }
  8.  
  9. class Actor {
  10. private $id;
  11.  
  12. private $firstName;
  13. private $lastName;
  14.  
  15. private $films; // ponownie, tablica/kolekcja jednak tym razem obiektów Film
  16. }
Do tego, każda z tych klas powinna mieć zestaw funkcji get/set/has/add/etc dla każdej z właściowości.
viking
Ale w sumie masz to dokładnie opisane w dokumentacji http://docs.phalconphp.com/en/latest/refer...-between-models więc jaki jest problem? Skoro już wykorzystujesz jakiś FW to w pierwszej kolejności postaraj się szukać rozwiązań dla niego specyficznych.
WebCM
W Doctrine i ORM-ach w Javie klucze obce są mapowane na obiekty:
  1. public class Aktor
  2. {
  3. private int ID;
  4. private String imie;
  5. private Film film; //uprośćmy, że aktor gra tylko w 1 filmie
  6. }
  7.  
  8. //Do ID filmu odwołujemy się tak
  9. aktor.getFilm().getID()
  10.  
  11. //Ostatecznie z publicznymi polami
  12. aktor.film.ID

Prawda, że proste? Twórcy Phalcona obrali inną ścieżkę. Czy właściwą? Pole film otrzyma ID powiązanego rekordu zamiast obiektu klasy Film. W przykładach nazwy tabel i klas są w liczbie mnogiej. Najlepiej pokażę do na przykładach:
  1. $aktor = Aktorzy::findFirst($id);
  2. $nazwa_filmu = $aktor->filmy->nazwa; //filmy - nazwa klasy
  3. $id_filmu = $aktor->film_id; //film_id - nazwa kolumny w tabeli
  4. $id_filmu = $aktor->film; //a jak sobie nazwiemy tak kolumnę
Nie trzeba żadnych adnotacji i plików konfiguracyjnych. Wystarczy tylko ustawić wirtualne klucze obce, bo tego Phalcon sam nie odczyta z bazy. Oczywiście wszystko możemy zmienić po swojemu, a nawet dodać aliasy dla nazw pól i relacji. Tu powstają pytania:

1. Czy w porządnym ORM potrzebujemy pól z ID powiązanych rekordów?
2. Czy z tego powodu zmieniać nazwy kolumn z "film" na "filmId" lub "film_id"?
Crozin
Nie, nie potrzebujemy. Wręcz nie powinniśmy się zajmować tym jak powiązane są ze sobą obiekty w naszej hierarchii. Klucze obce to wewnętrzny mechanizm bazy danych, który nie powinien być odzwierciedlony w modelu obiektowym. Co innego ID pojedynczego obiektu - to już jest zwykła właściwość obiektu.

W Doctrine, które bardzo, bardzo mocno bazuje na JPA (Java) stosuje się model DataMapper oraz kieruje się zasadą, którą nazwałbym "pierwszeństwem modelu obiektowego, nad bazodanowym". IMHO jak do tej pory, najlepsze rozwiązanie jakie wymyślono w kwestii ORM-ów.
WebCM
Phalcon jest podatny na SQL Injection w ograniczonym zakresie. Przykład:
  1. //localhost/kontroler/akcja/1=1
  2. public function akcjaAction($id)
  3. {
  4. $user = User::findFirst($id);
  5. }

Rozwiązania tego problemu:

1. Sprawdzać, czy $id zawiera cyfry - ctype_digit
2. Ręcznie ustawić ścieżki w klasie Router, aby parametr zawierał tylko cyfry
3. Napisać własne metody typu User::getById($id) i tam zadbać o bezpieczeństwo
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.