Doctrine ORM (Object-Relational Mapping) ist ein leistungsstarkes Tool, das die Verwaltung von Datenbanken in Symfony-Anwendungen erheblich vereinfacht. Es ermöglicht Entwicklern, Datenbankoperationen durch die Arbeit mit PHP-Objekten durchzuführen, anstatt direkt SQL-Abfragen zu schreiben. In diesem Artikel werden wir die folgenden Themen ausführlich behandeln:
- Einrichtung von Doctrine
- Erstellen von Entitäten und Zuordnung zu Datenbanktabellen
- Datenbankmigrationen mit Doctrine Migrations
- CRUD-Operationen
- Abfragen mit Doctrine Query Builder und DQL
- Verwaltung von Beziehungen (OneToOne, OneToMany, ManyToMany)
- Arbeiten mit Repositories
1. Einrichtung von Doctrine
1.1 Installation von Doctrine
Wenn Sie ein neues Symfony-Projekt mit dem --full
Flag erstellen, ist Doctrine normalerweise bereits installiert. Falls nicht, können Sie es mit dem folgenden Composer-Befehl hinzufügen:
composer require orm
Dieser Befehl installiert das Doctrine ORM und die notwendigen Pakete.
1.2 Datenbankkonfiguration
Doctrine benötigt Informationen über die Datenbankverbindung. Diese werden in der .env
Datei Ihres Projekts definiert.
Beispiel .env
Datei:
DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name"
- db_user: Ihr Datenbankbenutzername
- db_password: Ihr Datenbankpasswort
- 127.0.0.1: Host der Datenbank (lokal)
- 3306: Standardport für MySQL
- db_name: Name Ihrer Datenbank
1.3 Erstellen der Datenbank
Nachdem Sie die Datenbankverbindung konfiguriert haben, können Sie die Datenbank erstellen:
php bin/console doctrine:database:create
2. Erstellen von Entitäten und Zuordnung zu Datenbanktabellen
2.1 Was ist eine Entität?
Eine Entität ist eine PHP-Klasse, die ein Datenbankobjekt repräsentiert. Sie enthält Eigenschaften, die den Spalten der Datenbanktabelle entsprechen, und Annotations, die die Zuordnung definieren.
2.2 Erstellen einer Entität mit dem Maker-Bundle
Das Maker-Bundle vereinfacht die Erstellung von Entitäten.
Installation des Maker-Bundles (falls nicht bereits installiert):
composer require symfony/maker-bundle --dev
Erstellen einer Entität:
php bin/console make:entity Product
Der Befehl startet einen interaktiven Prozess.
Beispiel:
New property name (press <return> to stop adding fields):
> name
Field type (enter ? to see all types) [string]:
>
Field length [255]:
>
Can this field be null in the database (nullable) (yes/no) [no]:
>
Add another property? Enter the property name (or press <return> to stop adding fields):
> price
Field type (enter ? to see all types) [string]:
> float
Can this field be null in the database (nullable) (yes/no) [no]:
>
Add another property? Enter the property name (or press <return> to stop adding fields):
>
2.3 Die generierte Entitätsklasse
Nach dem Erstellen sieht die Klasse Product
etwa so aus:
// src/Entity/Product.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity()
*/
class Product
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
/**
* @ORM\Column(type="float")
*/
private $price;
// Getter und Setter Methoden...
public function getId(): ?int
{
return $this->id;
}
// ... weitere Getter und Setter
}
2.4 Mappings und Annotations
- @ORM\Entity(): Kennzeichnet die Klasse als Entität.
- @ORM\Id: Markiert die Eigenschaft als Primärschlüssel.
- @ORM\GeneratedValue: Der Wert wird automatisch generiert (Auto-Inkrement).
- @ORM\Column: Definiert die Spalte in der Datenbank mit Typ und optionalen Eigenschaften.
3. Datenbankmigrationen mit Doctrine Migrations
3.1 Installation von Doctrine Migrations
Doctrine Migrations verwaltet Schemaänderungen in der Datenbank.
composer require doctrine/migrations
3.2 Erstellen einer Migration
Nachdem Sie Ihre Entitäten erstellt oder geändert haben, müssen Sie die Änderungen in die Datenbank übertragen.
Schritt 1: Migration generieren
php bin/console make:migration
Dieser Befehl erzeugt eine neue Migrationsdatei im Verzeichnis migrations/
.
Schritt 2: Migration ausführen
php bin/console doctrine:migrations:migrate
Bestätigen Sie die Ausführung, wenn Sie dazu aufgefordert werden.
3.3 Inhalt einer Migrationsdatei
Eine Migrationsdatei enthält Methoden up()
und down()
, die SQL-Anweisungen zum Aktualisieren und Rückgängigmachen der Schemaänderungen enthalten.
Beispiel:
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE product (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, price DOUBLE PRECISION NOT NULL, PRIMARY KEY(id))');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE product');
}
4. CRUD-Operationen
CRUD steht für Create, Read, Update, Delete. Doctrine ermöglicht es, diese Operationen einfach durchzuführen.
4.1 Entity Manager
Der Entity Manager (EntityManagerInterface
) ist für das Verwalten von Entitäten zuständig.
Einbindung in den Controller:
use Doctrine\ORM\EntityManagerInterface;
public function index(EntityManagerInterface $entityManager)
{
// ...
}
4.2 Erstellen (Create)
Beispiel:
public function create(EntityManagerInterface $entityManager)
{
$product = new Product();
$product->setName('Laptop');
$product->setPrice(999.99);
$entityManager->persist($product);
$entityManager->flush();
return new Response('Produkt erstellt mit der ID ' . $product->getId());
}
- persist(): Bereitet das Objekt zum Speichern vor.
- flush(): Führt die Änderungen in der Datenbank aus.
4.3 Lesen (Read)
Beispiel:
use App\Repository\ProductRepository;
public function show(ProductRepository $productRepository, $id)
{
$product = $productRepository->find($id);
if (!$product) {
throw $this->createNotFoundException('Produkt nicht gefunden');
}
return new Response('Produkt: ' . $product->getName());
}
4.4 Aktualisieren (Update)
Beispiel:
public function update(EntityManagerInterface $entityManager, $id)
{
$product = $entityManager->getRepository(Product::class)->find($id);
if (!$product) {
throw $this->createNotFoundException('Produkt nicht gefunden');
}
$product->setPrice(899.99);
$entityManager->flush();
return new Response('Produkt aktualisiert');
}
4.5 Löschen (Delete)
Beispiel:
public function delete(EntityManagerInterface $entityManager, $id)
{
$product = $entityManager->getRepository(Product::class)->find($id);
if (!$product) {
throw $this->createNotFoundException('Produkt nicht gefunden');
}
$entityManager->remove($product);
$entityManager->flush();
return new Response('Produkt gelöscht');
}
5. Abfragen mit Doctrine Query Builder und DQL
5.1 Doctrine Query Language (DQL)
DQL ist eine objektorientierte Abfragesprache, die SQL ähnelt, aber mit Entitäten arbeitet.
Beispiel:
$entityManager = $this->getDoctrine()->getManager();
$query = $entityManager->createQuery(
'SELECT p
FROM App\Entity\Product p
WHERE p.price > :price'
)->setParameter('price', 500);
$products = $query->getResult();
5.2 Query Builder
Der Query Builder ermöglicht den schrittweisen Aufbau von Abfragen.
Beispiel:
$repository = $entityManager->getRepository(Product::class);
$query = $repository->createQueryBuilder('p')
->where('p.price > :price')
->setParameter('price', 500)
->orderBy('p.price', 'ASC')
->getQuery();
$products = $query->getResult();
5.3 Verwendung von Parametern
- setParameter(): Bindet einen Wert an einen Platzhalter.
- Sicherheitsvorteil: Schützt vor SQL-Injection.
5.4 Paginierung
Mit dem Query Builder können Sie Paginierung einfach implementieren.
Beispiel:
$page = 1;
$pageSize = 10;
$query = $repository->createQueryBuilder('p')
->setFirstResult(($page - 1) * $pageSize)
->setMaxResults($pageSize)
->getQuery();
$products = $query->getResult();
6. Verwaltung von Beziehungen (OneToOne, OneToMany, ManyToMany)
6.1 OneToOne-Beziehung
Beispiel:
Entitäten:
- User
- Profile
User.php
/**
* @ORM\OneToOne(targetEntity=Profile::class, cascade={"persist", "remove"})
* @ORM\JoinColumn(nullable=false)
*/
private $profile;
Profile.php
/**
* @ORM\OneToOne(targetEntity=User::class, inversedBy="profile")
* @ORM\JoinColumn(nullable=false)
*/
private $user;
6.2 OneToMany und ManyToOne-Beziehungen
Beispiel:
Entitäten:
- Category
- Product
Category.php
/**
* @ORM\OneToMany(targetEntity=Product::class, mappedBy="category")
*/
private $products;
Product.php
/**
* @ORM\ManyToOne(targetEntity=Category::class, inversedBy="products")
* @ORM\JoinColumn(nullable=false)
*/
private $category;
6.3 ManyToMany-Beziehung
Beispiel:
Entitäten:
- Product
- Tag
Product.php
/**
* @ORM\ManyToMany(targetEntity=Tag::class, inversedBy="products")
* @ORM\JoinTable(name="product_tag")
*/
private $tags;
Tag.php
/**
* @ORM\ManyToMany(targetEntity=Product::class, mappedBy="tags")
*/
private $products;
6.4 Arbeiten mit Beziehungen
Hinzufügen eines Produkts zu einer Kategorie:
$category = new Category();
$category->setName('Elektronik');
$product = new Product();
$product->setName('Smartphone');
$product->setPrice(699.99);
$product->setCategory($category);
$entityManager->persist($category);
$entityManager->persist($product);
$entityManager->flush();
Hinzufügen von Tags zu einem Produkt:
$tag1 = new Tag();
$tag1->setName('Neu');
$tag2 = new Tag();
$tag2->setName('Angebot');
$product->addTag($tag1);
$product->addTag($tag2);
$entityManager->persist($tag1);
$entityManager->persist($tag2);
$entityManager->persist($product);
$entityManager->flush();
7. Arbeiten mit Repositories
7.1 Was ist ein Repository?
Ein Repository ist eine Klasse, die spezielle Abfragefunktionen für eine Entität bereitstellt. Standardmäßig generiert Doctrine für jede Entität ein Repository.
7.2 Erstellen eines benutzerdefinierten Repositories
Beim Erstellen einer Entität können Sie angeben, dass ein Repository erstellt werden soll.
Beispiel:
php bin/console make:entity --regenerate
Wählen Sie die Entität und geben Sie den Repository-Namen an.
7.3 Verwendung des Repositories
Beispiel:
// src/Repository/ProductRepository.php
namespace App\Repository;
use App\Entity\Product;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
class ProductRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Product::class);
}
public function findExpensiveProducts($price)
{
return $this->createQueryBuilder('p')
->andWhere('p.price > :price')
->setParameter('price', $price)
->orderBy('p.price', 'DESC')
->getQuery()
->getResult();
}
}
Verwendung im Controller:
public function expensiveProducts(ProductRepository $productRepository)
{
$products = $productRepository->findExpensiveProducts(1000);
// ...
}
7.4 Weitere Methoden des Repositories
- find($id): Findet eine Entität anhand der ID.
- findOneBy($criteria): Findet eine Entität anhand von Kriterien.
- findBy($criteria): Findet mehrere Entitäten anhand von Kriterien.
- findAll(): Gibt alle Entitäten zurück.
Zusammenfassung
Doctrine ORM integriert sich nahtlos in Symfony und ermöglicht eine effiziente und objektorientierte Arbeit mit Datenbanken. Durch das Verständnis der folgenden Schlüsselkonzepte können Sie leistungsfähige und skalierbare Anwendungen entwickeln:
- Einrichtung von Doctrine: Installieren und Konfigurieren der Datenbankverbindung.
- Entitäten und Mappings: Erstellen von PHP-Klassen, die Datenbanktabellen repräsentieren.
- Migrationen: Verwalten von Datenbankschemata über Migrationen.
- CRUD-Operationen: Durchführen von grundlegenden Datenbankoperationen.
- Abfragen: Verwenden von DQL und Query Builder für komplexe Abfragen.
- Beziehungen: Modellieren von Datenbankbeziehungen zwischen Entitäten.
- Repositories: Kapseln von Abfragefunktionen für wiederverwendbaren Code.