Allgemein

Symfony 2.8 Teil 2 – Bundles, Controller und Entites

Willkommen zum zweiten Teil meines Symfony 2.8 Tutorials.

Heute lernst du die Kernelemente von Symfony kennen um eine Anwendung zu erstellen. Außerdem zeige ich dir, wie du das CSS-Framework Bootstrap von Twitter integrierst.

Konfigurations-Format

Vorweg möchte ich anmerken dass es bei Symfony mehrere Konfigurations-Formate gibt. In diesem Tutorial verwenden wir grundsätzlich „annotation“.
Dies sind Kommentare welche über Methoden, Variablen etc. stehen. Diese haben Ähnlichkeit mit PHP-Doc-Kommentaren. Als Beispiel:

class DefaultController extends Controller
{
    /**
     * @Route("/", name="homepage")
     * @Template()
     */
    public function indexAction()
    {
        // ...
    }
}

Bundles, Controller und Entities

Bundles

Bundles sind eigenständige Pakete welche für einen bestimmten Bereich einer Software zuständig sind. Sinn dahinter ist, dass man die Bundles aus dem eigentlichen Projekt heraustrennen und in einem anderen Projekt weiterverwenden kann.
Der Bundlename muss immer mit Bundle enden.

Zur Ver­deut­li­chung folgendes Beispiel:
Du möchtest eine Website erstellen (nennen wir sie mal EierlegendeWollmilchSau) welche als Shop, CMS und Blog dienen soll.

In deinem Symfony2 Projekt erstellst du hierzu drei Bundles im selben Namespace (EWS).

EWS\ShopBundle
EWS\CMSBundle
EWS\BlogBundle

Bundles erstellt man in der interaktiven Konsole mit dem Befehl
php app/console generate:bundle --namespace=namespacename\BeispielBundle

Controller

Controller sind die Klassen welche die HTTP-Anfragen verwalten. Damit dies funktioniert wird innerhalb der Klassen pro HTTP-Anforderung eine Methode (so genannte Action) erstellt.
Damit Symfony weiß welche Methode wann ausgeführt werden muss, wird in den Annotations per @Route("/routenPfad") festgelegt bei welchem Aufruf die Methode ausgeführt werden soll.
Man kann Variablen mittels geschweiften klammern definieren(@Route(„/benutzer/{benutzername}“)).
Die Actionnamen müssen mit Action und Controllernamen müssen mit Controller enden.

Controller erstellt man in der interaktiven Konsole mit dem Befehl
php app/console generate:controller --controller=NamespaceBeispielBundle:Blogpost

Entities

Entities sind die Datenbank-Modelle. Doctrine erstellt aus den Klassen automatisch das Datenbankschema. Tabellennamen, Feldnamen, Beziehungen usw. werden in den Annotations definiert. Des weiteren gibt es sogenannte Repositories welche dem EntityManager (dazu später mehr) weitere Methoden neben den Gettern/Settern spendieren.
Repositories werden deshalb erstellt, damit man sinnvolle Abfragen (z.B. Hole alle Benutzer die sich vor dem TT.MM.YYYY registriert haben) nicht mehrfach schreiben muss und somit an verschiedenen Stellen im Quellcode verwenden kann.

Entities erstellt man in der interaktiven Konsole mit dem Befehl
php app/console doctrine:generate:entity --entity=NamespaceBeispielBundle:Entity

Bundle und Controller erstellen

Das erste Bundle

Jetzt ist es an der Zeit dass du dein erstes Bundle generierst. Öffne hierzu die Git-Bash im htdocs/dvd-db-Ordner.
Tippe nun den Befehl php app/console generate:bundle --namespace=DH\DvdDbBundle ein.

Dir werden nun folgende Fragen gestellt (diese können alle mit Enter beantwortet werden):
Are you planning on sharing this bundle across multiple applications? [no]
Wenn du hier mit yes Antworten würdest, würde könnte man das Bundle heraustrennen und als Paket bspw. auf Github anbieten.
Hierbei wird bspw. eine eigenständige composer.json Datei erstellt welche die Abhängigkeiten enthält. Für unser vorhaben benötigen wir dies nicht

Bundle name [DHDvdDbBundle]:
Der Bundlename. Dieser setzt sich aus Namespace (DH für Developer-Heaven), dem Namen für das Bundle (DvdDb) und einem abschließenden Bundle zusammen.

Target Directory [src/]:
Der Pfad in welchem das Bundle gespeichert werden soll.

Configuration format (annotation, yml, xml, php) [annotation]:
Hierbei handelt es sich um das am Anfang erwähnte Konfigurationsformat.

Sobald das Bundle generiert wurde und es zu keinem Fehler kam, solltest du nun die folgende Nachricht angezeigt bekommen:
Everything is OK! Now get to work :).

Der erste Controller

Jetzt erstellst du mit dem folgenden Befehl innerhalb des DvdDbBundle den MovieController. Dieser Controller wird später das verwalten der DVDs ermöglichen.
php app/console generate:controller --controller=DHDvdDbBundle:Movie

Controller name [DHDvdDbBundle:Movie]:
Der Controller Name. Dieser wird ohne Controller am Ende angegeben

Routing format (php, xml, yml, annotation) [annotation]:
Siehe oben

Template format (twig, php) [twig]:
Das Format für die Templates. Ich verwende grundsätzlich twig.

Die weiteren Ausgaben prüfen und mit Enter bestätigen sodass wieder am Ende die Erfolgsmeldung kommt:
Everything is OK! Now get to work :). – An die Arbeit, das werden wir jetzt auch tun!

Bootstrap in das Symfony Projekt integrieren

Jetzt ist es Zeit das CSS-Framework Bootstrap zu integrieren. Ich habe mich für Bootstrap entschieden da dieses kostenlos und einfach zu integrieren ist. Obendrein sieht es auch noch richtig gut aus.

Gehe auf GetBootstrap.com und lade dir Bootstrap herunter. Die aktuelle Version zum Zeitpunkt des Tutorials ist v3.3.6.
Anschließend gehst du in deinen htdocs-Ordner der XAMPP-Installation. Dort solltest du nun den Ordner dvd-db finden. Navigiere dort hinein und dann in das Verzeichnis src/DHDvdDbBundle/Resources. Hier erstellst du nun einen Ordner public. In den public-Ordner entpackst du nun bootstrap. Achte darauf dass der entpackte Ordner auch bootstrap heißt und nicht wie in meinem Fall bootstrap-3.3.6-dist.
Danach erstellst du im public Ordner noch den Ordner dvd-db/css dvd-db/js
Nun solltest du nun die folgende Struktur haben:

htdocs
 │
 └─dvd-db
   │
   └─src
     │
     └─DHDvdDbBundle
        │
        └─Resources
           |
           └─public
              |
              ├─bootstrap
              │  └─css
              │  ├─fonts
              │  └─js
              │
              └─dvd-db
                ├─css
                └─js

Aber der src/-Ordner ist doch gar nicht öffentlich…?!
Stimmt, der Grund warum die Resourcen nun im source ordner gespeichert werden ist einfach. Stell dir vor du willst dein Bundle shareable (bspw. auf Packagist veröffentlichen) machen, dann müssten diejenigen die das Bundle nutzen jedesmal Bootstrap in den web-Ordner laden was natürlich umständlich ist und ggf. auch nicht die korrekte Version vorhanden ist.

Damit die Resourcen nun in den web-Ordner geladen werden muss folgender Befehl ausgeführt werden:
php app/console assets:install --symlink --relative

Damit ist Bootstrap integriert und du kannst es in den Templates verwenden.

Coden, Coden, Coden…

Nun darfst du endlich Eclipse PHPStorm bzw. deine bevorzugte IDE starten und ein Projekt aus dem vorhanden Source Code erstellen. An dieser Stelle erwarte ich dass du dich mit deiner IDE auskennst und das selbständig erledigen kannst. Falls es Probleme hierbei gibt kannst du gerne in den Kommentaren fragen 😉

Als erstes Navigierst du in den Ordner dvd-db/src/DHDvdDbBundle/Controller und löschst die Datei DefaultController.php den Controller brauchen wir nicht! Außerdem löschst du das Verzeichnis dvd-db/src/DHDvdDbBundle/Resources/views/Default/

Unser Basis Template

Ich möchte hier Anmerken dass wir uns im übernächsten Teil der Serie intensiver mit Twig auseinandersetzen und ich daher in diesem Part auf die Template-Codes nicht eingehe.
Navigiere in den Ordner dvd-db/src/DHDvdDbBundle/Resources/views und lege hier eine neue Datei mit dem namen base.html.twig an.
Diese füllst du mit folgendem Inhalt:

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset={{ _charset }}"/>
        <meta name="robots" content="noindex,nofollow"/>
        <title>{% block title %}DVD-Datenbank{% endblock %}</title>
        <link rel="stylesheet" href="{{ asset('bundles/dhdvddb/bootstrap/css/bootstrap.min.css') }}"/>
        <link rel="stylesheet" href="{{ asset('bundles/dhdvddb/bootstrap/css/bootstrap-theme.min.css') }}"/>
        <link rel="stylesheet" href="{{ asset('bundles/dhdvddb/dvd-db/css/main.css') }}"/>
        {% block head %}{% endblock %}
    </head>
    <body role="document">
 
        <!-- navbar -->
        <nav class="navbar navbar-default">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed"
                            data-toggle="collapse" data-target="#menu-toggle">
                        <span class="sr-only"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand" href="/">DVD-Datenbank</a>
                </div>
 
                <!-- Collect the nav links, forms, and other content for toggling -->
                <div class="collapse navbar-collapse" id="menu-toggle">
                    <ul class="nav navbar-nav">
                        <li><a href="#">Film hinzufügen</a></li>
                    </ul>
                </div>
                <!-- /.navbar-collapse -->
            </div>
            <!-- /.container-fluid -->
        </nav>
        <!-- /navbar -->
 
        <div class='container'>
            {% block body %}{% endblock %}
        </div>
 
        {% block javascripts %}
            <script src="//code.jquery.com/jquery-1.9.1.min.js"></script>
            <script src="{{ asset('bundles/dhdvddb/bootstrap/js/bootstrap.min.js') }}" type="text/javascript"></script>
        {% endblock %}
 
    </body>
 
</html>

Diese Datei speicherst und schließt du.

Die Startseite

Ich habe mir überlegt als Startseite das Filmverzeichnis anzuzeigen. Erstelle hierzu das Verzeichnis dvd-db/src/DHDvdDbBundle/Resources/views/Movie/ und darin die Datei index.html.twig mit folgendem Inhalt:

{% extends 'DHDvdDbBundle::base.html.twig' %}
{% block body %}
	<div class="row">			
		<div class="col-md-12">
			<div class="panel panel-default">
				<div class="panel-heading">Suche</div>
					<div class="panel-body">
					<div class="input-group">
						<input type="text" class="form-control">
						<div class="input-group-btn">
							<button class="btn btn-default" type="button">Go!</button>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>
	<div class="row">			
		<div class="col-md-12">
			<div class="panel panel-default">
				<div class="panel-heading">Filme</div>
				<div class="panel-body">
					<p>Noch keine Filme vorhanden</p>
				</div>
				<table class="table">
					<thead>
						<tr>
							<th>Name</th>
							<th>Sprache</th>
							<th>Schauspieler</th>
							<th>Jahr</th>
							<th>IMDB ID</th>
						</tr>
					</thead>
				</table>
			</div>
		</div>
	</div>
{% endblock %}

Damit hast du nun deine (noch statische) Startseite erstellt. Jetzt musst du nur noch deinem Controller sagen wann das Template geladen werden soll. Das zeige ich dir jetzt.

Die Action im MovieController

Öffne nun MovieController welcher im Verzeichnis src/DHDvdBundle/Controller/ liegt.
Dieser sollte aktuell folgenden Code enthalten:

<?php
 
namespace DHDvdDbBundle\Controller;
 
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 
class MovieController extends Controller
{
}

In der MovieController-Klasse erstellst du nun die Methode indexAction() welche keinen Parameter erwartet aber ein leeres Array zurückgibt. Als Annotations fügst du @Route("/") und @Template() hinzu:

/**
 * @Route("/")
 * @Template()
 */
function indexAction()
{
    return array();
}

@Route("/") bedeutet dass diese Action aufgerufen wird, wenn die URL „/“ entspricht, also dem docroot. Würde hier stattdessen „/foo“ stehen, wäre der Aufruf damit die Action ausgeführt wird „http://dvd-db.local/app_dev.php/foo“.
@Template sagt Symfony, dass für diese Action ein Template im Resources Verzeichnis existiert (demnach das oben erstellte index.html.twig-Template). Außerdem wird hierbei dann von der Action ein Array mit allen Variablen erwartet welche im Template verwendet werden. Da du (noch) keine Variablen im Template verwendest, wird hier ein leeres Array zurückgegeben.

Falls dein Editor die notwendigen Namespaces nicht automatisch hinzugefügt hat, musst du diese noch manuel unter
use Symfony\Bundle\FrameworkBundle\Controller\Controller; hinzufügen:

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

Wie siehts aus?

Jetzt darfst du sehen was du gemacht hast. Speichere hierzu alle bearbeiteten Dateien und öffne http://dvd-db.local/app_dev.php/
Wenn du alles richtig gemacht hast, solltest du nun folgende Seite vor dir sehen:
Controller indexAction

Deine erste Entity

Damit in dieser Teil der Serie nicht übermäßig Lang wird, kümmern wir uns jetzt erst einmal um die DVD Entity. Öffne hierzu wieder deine Gitbash im dvd-db-Verzeichnis.
Tippe nun folgenden Befehl ein php app/console doctrine:generate:entity --entity=DHDvdDbBundle:Dvd.
Die ersten beiden Fragen kennst du ja bereits. Diese beantwortest du einfach mit Enter.
Anschließend wirst du nacheinander gefragt welche Felder die Entity bekommen soll:
Entity erstellen

Innerhalb des Templates kannst du erkennen welche Felder benötigt werden. Erstelle diese nun wie folgt nacheinander:

fieldnameField typeField lengthIs nullableUnique
namestring100falsefalse
languagestring50falsefalse
actorsarrayfalsefalse
yearintegerfalsefalse
imdb_idstring15falsefalse

Nach dem du alle Felder angelegt hast, drückst du zwei mal Enter um die Eingabe abzuschließen.
Jetzt solltest du die mittlerweile bekannte Meldung Everything is OK! Now get to work :). erhalten.

Die Entity findest du unter src/DHDvdBundle/Entites/Dvd.php

Datenbankschema aktualisieren

Dein PHP-Modell ist nun erstellt. Jetzt soll das Modell aber auch in die Datenbank geschrieben werden. Gib hierzu folgenden Befehl in deiner Git-Bash ein:
php app/console doctrine:schema:update

Symfony wird jetzt meckern, das ist auch vollkommen richtig so:
Schema Update

Man sollte kein Datenbank-Update durchführen ohne wissen was überhaupt aktualisiert wird.
Daher prüfen wir mit php app/console doctrine:schema:update --dump-sql was eigentlich ausgeführt wird.
Sind die Queries in Ordnung, kann man mit php app/console doctrine:schema:update --force die Datenbankstruktur aktualisieren.

Schlusswort

Der heutige Teil ist etwas länger als Erwartet geworden, allerdings haben wir jetzt die Basis geschaffen welche wir benötigen, um im nächsten Part (am DonnerstagSamstag) die Logik zu schaffen.
Ich werde dir dann zeigen wie man automatisiert Formulare generieren lassen kann (CRUD) und wie die Film-Liste sich mit leben füllt.

Bis dahin wünsche ich dir viel Spaß beim rum probieren 🙂

~Julian

Zurück zu „Teil 1 – Der Einstieg“

Weiter zu „Teil 3 – Formulare“


20x gelesen

Print Friendly, PDF & Email

2 Kommentare

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.