├── slides
├── outro
│ └── thanks.html
├── doctrine
│ └── beginer.html
├── introduction
│ ├── intro.html
│ ├── i-am-marco.html
│ └── i-work-for.html
└── list.json
├── .gitignore
├── assets
├── img
│ ├── cqrs.png
│ ├── crud.jpg
│ ├── logo.png
│ ├── shit.jpg
│ ├── awesome.png
│ ├── ocramius.gif
│ ├── sandwich.png
│ ├── cqrs-diagram.png
│ ├── github-white.png
│ ├── amusing-kitten.gif
│ ├── bullshit-stamp.png
│ ├── twitter.svg
│ ├── gh.svg
│ ├── zf-logo.svg
│ ├── doctrine.svg
│ ├── github.svg
│ └── roave.svg
├── font
│ ├── TheSansMonoCondensed-SemiLight-webfont.eot
│ ├── TheSansMonoCondensed-SemiLight-webfont.ttf
│ ├── TheSansMonoCondensed-SemiLight-webfont.woff
│ ├── TheSansMonoCondensed-SemiLight-webfont.woff2
│ ├── stylesheet.css
│ ├── specimen_files
│ │ ├── easytabs.js
│ │ ├── grid_12-825-55-15.css
│ │ └── specimen_stylesheet.css
│ └── TheSansMonoCondensed-SemiLight-webfont.svg
└── style.css
├── image
├── common_icon.png
├── Doctrine-Logo.png
├── database-status.png
└── databaseMapperSketch.gif
├── bower.json
├── README.md
├── .gitmodules
├── examples
├── 1.php
├── 7.php
├── 2.php
├── 11.php
├── 5.php
├── 3.php
├── 8.php
├── 6.php
├── 9.php
├── 10.php
└── 4.php
├── style
└── slippy-styled.css
├── doctrine-cli.php
├── templates
├── _section.html
└── _index.html
├── package.json
├── library
└── Entity
│ ├── Greeting.php
│ ├── User.php
│ └── Comment.php
├── bootstrap.php
├── js
└── loadhtmlslides.js
├── css
└── source
│ └── theme.scss
├── Gruntfile.coffee
└── index.html
/slides/outro/thanks.html:
--------------------------------------------------------------------------------
1 |
Thanks!
2 |
--------------------------------------------------------------------------------
/slides/doctrine/beginer.html:
--------------------------------------------------------------------------------
1 | Doctrine best pratices
2 |
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bower_components
3 | css/theme.css
4 | css/theme.css.map
--------------------------------------------------------------------------------
/slides/introduction/intro.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/img/cqrs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/cqrs.png
--------------------------------------------------------------------------------
/assets/img/crud.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/crud.jpg
--------------------------------------------------------------------------------
/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/logo.png
--------------------------------------------------------------------------------
/assets/img/shit.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/shit.jpg
--------------------------------------------------------------------------------
/image/common_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/image/common_icon.png
--------------------------------------------------------------------------------
/assets/img/awesome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/awesome.png
--------------------------------------------------------------------------------
/assets/img/ocramius.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/ocramius.gif
--------------------------------------------------------------------------------
/assets/img/sandwich.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/sandwich.png
--------------------------------------------------------------------------------
/image/Doctrine-Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/image/Doctrine-Logo.png
--------------------------------------------------------------------------------
/image/database-status.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/image/database-status.png
--------------------------------------------------------------------------------
/slides/introduction/i-am-marco.html:
--------------------------------------------------------------------------------
1 | I'm Marco!
2 |
3 |
--------------------------------------------------------------------------------
/assets/img/cqrs-diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/cqrs-diagram.png
--------------------------------------------------------------------------------
/assets/img/github-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/github-white.png
--------------------------------------------------------------------------------
/assets/img/amusing-kitten.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/amusing-kitten.gif
--------------------------------------------------------------------------------
/assets/img/bullshit-stamp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/img/bullshit-stamp.png
--------------------------------------------------------------------------------
/image/databaseMapperSketch.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/image/databaseMapperSketch.gif
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ShittyCQRS explained",
3 | "version": "1.0.0",
4 | "dependencies": {
5 | "reveal.js": "~2.6.1"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/assets/font/TheSansMonoCondensed-SemiLight-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/font/TheSansMonoCondensed-SemiLight-webfont.eot
--------------------------------------------------------------------------------
/assets/font/TheSansMonoCondensed-SemiLight-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/font/TheSansMonoCondensed-SemiLight-webfont.ttf
--------------------------------------------------------------------------------
/assets/font/TheSansMonoCondensed-SemiLight-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/font/TheSansMonoCondensed-SemiLight-webfont.woff
--------------------------------------------------------------------------------
/assets/font/TheSansMonoCondensed-SemiLight-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/HEAD/assets/font/TheSansMonoCondensed-SemiLight-webfont.woff2
--------------------------------------------------------------------------------
/slides/introduction/i-work-for.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/slides/list.json:
--------------------------------------------------------------------------------
1 | [
2 | "introduction/intro.html",
3 | "introduction/i-am-marco.html",
4 | "introduction/i-work-for.html",
5 |
6 | "doctrine/beginer.html",
7 | "outro/thanks.html"
8 | ]
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This repository should act as a simple introductive set of slides for newcomers of the Doctrine 2 Object Relational Mapper.
2 |
3 | You can read the resulting slides at http://marco-pivetta.com/doctrine2-orm-tutorial/
4 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "vendor/slippy"]
2 | path = vendor/slippy
3 | url = https://github.com/Seldaek/slippy.git
4 | [submodule "vendor/syntax-highlighter"]
5 | path = vendor/syntax-highlighter
6 | url = https://github.com/alexgorbatchev/SyntaxHighlighter.git
7 |
--------------------------------------------------------------------------------
/examples/1.php:
--------------------------------------------------------------------------------
1 | persist($greeting);
13 |
14 | //Flushing all changes to database
15 | $em->flush();
16 |
17 | echo 'OK!';
--------------------------------------------------------------------------------
/examples/7.php:
--------------------------------------------------------------------------------
1 | persist($user);
11 |
12 | $comment = new Comment('This is a sample post!');
13 | $em->persist($comment);
14 |
15 | $user->addComment($comment);
16 |
17 | //Flushing all changes to database
18 | $em->flush();
19 |
20 | echo 'OK!';
--------------------------------------------------------------------------------
/style/slippy-styled.css:
--------------------------------------------------------------------------------
1 | /*
2 | Document : slippy-styled
3 | Created on : 30.11.2011, 13:39:47
4 | Author : Marco Pivetta
5 | Description:
6 | Overrides to slippy and highlighter default stylesheet
7 | */
8 |
9 | .slide {
10 | top: 40%;
11 | }
12 |
13 | .slideContent {
14 | padding: 0;
15 | overflow:auto;
16 | }
17 |
18 | .pro {
19 | color: #008200;
20 | }
21 |
22 | .cons {
23 | color: #ff0000;
24 | }
25 |
--------------------------------------------------------------------------------
/doctrine-cli.php:
--------------------------------------------------------------------------------
1 | new EntityManagerHelper($em),
10 | 'conn' => new ConnectionHelper($em->getConnection())
11 | ));
12 | ConsoleRunner::run($helperSet);
--------------------------------------------------------------------------------
/examples/2.php:
--------------------------------------------------------------------------------
1 | find('Entity\Greeting', 1);
7 |
8 | if($greeting) {
9 | //The EntityManager has already provided us an object of type Entity\Greeting!
10 | echo 'Found a greeting (instance of ' . get_class($greeting)
11 | . ') with content ' . $greeting->getContent();
12 | }else{
13 | echo 'Couldn\'t find Greeting with id=1';
14 | }
--------------------------------------------------------------------------------
/examples/11.php:
--------------------------------------------------------------------------------
1 | 5: ' . PHP_EOL;
6 | $users = $em
7 | ->createQuery('SELECT u FROM Entity\User u JOIN u.comments c WHERE c.id > :id')
8 | ->setParameter('id', 5)
9 | ->getResult();
10 | echo 'Found ' . count($users) . ':' . PHP_EOL;
11 | foreach($users as $user) {
12 | echo ' ' . $user->getId() . ' => ' . $user->getLogin() . ' (' . get_class($user) . ')' . PHP_EOL;
13 | }
--------------------------------------------------------------------------------
/examples/5.php:
--------------------------------------------------------------------------------
1 | = 5 and id <= 10
6 | $greetings = $em
7 | ->createQuery('SELECT g FROM Entity\Greeting g WHERE g.id >= 5 AND g.id <= 10')
8 | ->getResult();
9 |
10 | //Displaying results
11 | echo 'Found ' . count($greetings) . ' Entity\Greeting:' . PHP_EOL;
12 | foreach($greetings as $greeting) {
13 | echo ' - ' . $greeting->getId() . ' => ' . $greeting->getContent() . PHP_EOL;
14 | }
--------------------------------------------------------------------------------
/templates/_section.html:
--------------------------------------------------------------------------------
1 | <% if (!_.isString(slide) && !_.isArray(slide) && _.isObject(slide)) { %>
2 | <% if (_.isString(slide.filename)) { %>data-<% if (slide.filename.indexOf('.html') !== -1) { %>html<% } else { %>markdown<% }%>="slides/<%= slide.filename %>"<% } %>>
3 | <% } %><% if (_.isString(slide)) { %>
4 | html<% } else { %>markdown<% }%>="slides/<%= slide %>">
5 | <% } %>
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "zf2-from-n00b-to-pro",
3 | "version": "1.0.0",
4 | "private": true,
5 | "devDependencies": {
6 | "coffeelint": "^1.9.3",
7 | "grunt": "~0.4.1",
8 | "grunt-coffeelint": "0.0.13",
9 | "grunt-contrib-connect": "^0.10.1",
10 | "grunt-contrib-copy": "^0.8.0",
11 | "grunt-contrib-jshint": "^0.11.1",
12 | "grunt-contrib-sass": "^0.9.2",
13 | "grunt-contrib-watch": "^0.6.1",
14 | "load-grunt-tasks": "^3.1.0"
15 | },
16 | "engines": {
17 | "node": ">=0.8.0"
18 | },
19 | "scripts": {
20 | "test": "grunt test"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/3.php:
--------------------------------------------------------------------------------
1 | find('Entity\Greeting', 1);
7 |
8 | if($greeting) {
9 | echo $greeting->getContent() . PHP_EOL;
10 | echo 'Changing the contents of found Greeting to "Hello Test!"' . PHP_EOL;
11 | //Using Entity\Greeting to set a new content for the $greeting!
12 | $greeting->setContent('Hello Test!');
13 | //Flushing changes to database (triggers SQL updates)
14 | $em->flush();
15 | echo 'Now try loading 2.php again!' . PHP_EOL;
16 | }else{
17 | echo 'Couldn\'t find Greeting with id=1';
18 | }
--------------------------------------------------------------------------------
/examples/8.php:
--------------------------------------------------------------------------------
1 | find('Entity\User', 1);
7 |
8 | if($user) {
9 | echo 'Found an Entity\User: ' . PHP_EOL
10 | . $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
11 | . 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
12 | foreach($user->getComments() as $comment) {
13 | echo ' ' . $comment->getId() . ' => ' . $comment->getContent()
14 | . ' (' . get_class($comment) . ')' . PHP_EOL;
15 | }
16 | } else {
17 | echo 'Could not find Entity\User with id=1';
18 | }
--------------------------------------------------------------------------------
/examples/6.php:
--------------------------------------------------------------------------------
1 | createQuery('SELECT g FROM Entity\Greeting g ORDER BY g.id DESC')
8 | ->setMaxResults(1) //we want only one result
9 | ->getResult();
10 |
11 | if(!empty($greetings)) {
12 | $greeting = reset($greetings);
13 | echo 'Found greeting with id "' . $greeting->getId()
14 | . '" and content "' . $greeting->getContent() . '"' . PHP_EOL;
15 | $em->remove($greeting);
16 | //Triggers delete
17 | $em->flush();
18 | echo 'Greeting deleted!' . PHP_EOL;
19 | } else {
20 | echo 'Could not find any Greeting' . PHP_EOL;
21 | }
--------------------------------------------------------------------------------
/assets/font/stylesheet.css:
--------------------------------------------------------------------------------
1 | /* Generated by Font Squirrel (http://www.fontsquirrel.com) on April 7, 2015 */
2 |
3 |
4 |
5 | @font-face {
6 | font-family: 'the_sans_mono_condensed-SLt';
7 | src: url('TheSansMonoCondensed-SemiLight-webfont.eot');
8 | src: url('TheSansMonoCondensed-SemiLight-webfont.eot?#iefix') format('embedded-opentype'),
9 | url('TheSansMonoCondensed-SemiLight-webfont.woff2') format('woff2'),
10 | url('TheSansMonoCondensed-SemiLight-webfont.woff') format('woff'),
11 | url('TheSansMonoCondensed-SemiLight-webfont.ttf') format('truetype'),
12 | url('TheSansMonoCondensed-SemiLight-webfont.svg#the_sans_mono_condensed-SLt') format('svg');
13 | font-weight: normal;
14 | font-style: normal;
15 |
16 | }
--------------------------------------------------------------------------------
/examples/9.php:
--------------------------------------------------------------------------------
1 | find('Entity\User', 1);
8 |
9 | if($user) {
10 | echo 'Found an Entity\User: ' . PHP_EOL
11 | . $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
12 | . 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
13 | echo 'Adding a Comment to the user';
14 | $comment = new Comment('Comment generated at ' . time());
15 | $em->persist($comment);
16 | $user->addComment($comment);
17 | $em->flush();
18 | echo 'Comment has been attached to the user, try 8.php!';
19 | } else {
20 | echo 'Could not find Entity\User with id=1';
21 | }
--------------------------------------------------------------------------------
/examples/10.php:
--------------------------------------------------------------------------------
1 | find('Entity\User', 1);
6 |
7 | if($user) {
8 | echo 'Found an Entity\User: ' . PHP_EOL
9 | . $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
10 | . 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
11 | if($comment = $user->getComments()->first()) {
12 | echo 'Removing the first attached comment!' . PHP_EOL;
13 | echo 'Removing comment with id=' . $comment->getId() . PHP_EOL;
14 | $em->remove($comment);
15 | $em->flush();
16 | } else {
17 | echo 'Could not find any comments to remove...' . PHP_EOL;
18 | }
19 | } else {
20 | echo 'Could not find Entity\User with id=1';
21 | }
--------------------------------------------------------------------------------
/assets/style.css:
--------------------------------------------------------------------------------
1 | .reveal section img {
2 | border: 0;
3 | background: none;
4 | box-shadow: 0 0 #000;
5 | }
6 |
7 | .php-color {
8 | color: #6C7EB7;
9 | }
10 |
11 | .proxy-color {
12 | color: #FC6A31;
13 | }
14 |
15 | .zf-color {
16 | color: #68B604;
17 | }
18 |
19 | .doctrine-color {
20 | color: #FC6A31;
21 | }
22 |
23 | .soothe .reveal .state-background {
24 | background: rgba(255, 255, 255, 0.6);
25 | }
26 |
27 | .pros-line:before, .cons-line:before {
28 | content: ' ';
29 | position: absolute;
30 | width: 0;
31 | height: 0;
32 | left: -40px;
33 | border: 12px solid transparent;
34 | }
35 |
36 | .pros-line:before {
37 | border-bottom-width: 22px;
38 | border-bottom-color: green;
39 | }
40 |
41 | .cons-line:before {
42 | border-top-width: 22px;
43 | border-top-color: red;
44 | }
45 |
--------------------------------------------------------------------------------
/examples/4.php:
--------------------------------------------------------------------------------
1 | getRepository('Entity\Greeting');
7 |
8 | //Finding all Entity\Greeting with content = "Hello World!"
9 | $worldGreetings = $repository->findBy(array('content' => 'Hello World!'));
10 |
11 | //Finding all Entity\Greeting with content = "Hello Test!"
12 | $testGreetings = $repository->findBy(array('content' => 'Hello Test!'));
13 |
14 | //Displaying results
15 | echo 'Found ' . count($worldGreetings) . ' "Hello World!" greetings:' . PHP_EOL;
16 | foreach($worldGreetings as $worldGreeting) {
17 | echo ' - ' . $worldGreeting->getId() . PHP_EOL;
18 | }
19 |
20 | echo 'Found ' . count($testGreetings) . ' "Hello Test!" greetings:' . PHP_EOL;
21 | foreach($testGreetings as $testGreeting) {
22 | echo ' - ' . $testGreeting->getId() . PHP_EOL;
23 | }
--------------------------------------------------------------------------------
/assets/font/specimen_files/easytabs.js:
--------------------------------------------------------------------------------
1 | (function($){$.fn.easyTabs=function(option){var param=jQuery.extend({fadeSpeed:"fast",defaultContent:1,activeClass:'active'},option);$(this).each(function(){var thisId="#"+this.id;if(param.defaultContent==''){param.defaultContent=1;}
2 | if(typeof param.defaultContent=="number")
3 | {var defaultTab=$(thisId+" .tabs li:eq("+(param.defaultContent-1)+") a").attr('href').substr(1);}else{var defaultTab=param.defaultContent;}
4 | $(thisId+" .tabs li a").each(function(){var tabToHide=$(this).attr('href').substr(1);$("#"+tabToHide).addClass('easytabs-tab-content');});hideAll();changeContent(defaultTab);function hideAll(){$(thisId+" .easytabs-tab-content").hide();}
5 | function changeContent(tabId){hideAll();$(thisId+" .tabs li").removeClass(param.activeClass);$(thisId+" .tabs li a[href=#"+tabId+"]").closest('li').addClass(param.activeClass);if(param.fadeSpeed!="none")
6 | {$(thisId+" #"+tabId).fadeIn(param.fadeSpeed);}else{$(thisId+" #"+tabId).show();}}
7 | $(thisId+" .tabs li").click(function(){var tabId=$(this).find('a').attr('href').substr(1);changeContent(tabId);return false;});});}})(jQuery);
--------------------------------------------------------------------------------
/library/Entity/Greeting.php:
--------------------------------------------------------------------------------
1 | setContent($content);
31 | }
32 |
33 | /**
34 | * @return int
35 | */
36 | public function getId() {
37 | return $this->id;
38 | }
39 |
40 | /**
41 | * @return string
42 | */
43 | public function getContent() {
44 | return $this->content;
45 | }
46 |
47 | /**
48 | * @param string $content
49 | */
50 | public function setContent($content) {
51 | $this->content = (string) $content;
52 | }
53 |
54 | }
--------------------------------------------------------------------------------
/assets/img/twitter.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/bootstrap.php:
--------------------------------------------------------------------------------
1 | register();
15 | $loader = new ClassLoader('EntityProxy', __DIR__ . '/library');
16 | $loader->register();
17 |
18 | //configuration
19 | $config = new Configuration();
20 | $cache = new Cache();
21 | $config->setQueryCacheImpl($cache);
22 | $config->setProxyDir(__DIR__ . '/library/EntityProxy');
23 | $config->setProxyNamespace('EntityProxy');
24 | $config->setAutoGenerateProxyClasses(true);
25 |
26 | //mapping (example uses annotations, could be any of XML/YAML or plain PHP)
27 | AnnotationRegistry::registerFile(__DIR__ . '/library/doctrine-orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
28 | $driver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver(
29 | new Doctrine\Common\Annotations\AnnotationReader(),
30 | array(__DIR__ . '/library/Entity')
31 | );
32 | $config->setMetadataDriverImpl($driver);
33 | $config->setMetadataCacheImpl($cache);
34 |
35 | //getting the EntityManager
36 | $em = EntityManager::create(
37 | array(
38 | 'driver' => 'pdo_sqlite',
39 | 'path' => 'database.sqlite'
40 | ),
41 | $config
42 | );
--------------------------------------------------------------------------------
/library/Entity/User.php:
--------------------------------------------------------------------------------
1 | comments = new ArrayCollection();
37 | $this->setLogin($login);
38 | }
39 |
40 | /**
41 | * @return int
42 | */
43 | public function getId() {
44 | return $this->id;
45 | }
46 |
47 | /**
48 | * @return string
49 | */
50 | public function getLogin() {
51 | return $this->login;
52 | }
53 |
54 | /**
55 | * @param string $login
56 | */
57 | public function setLogin($login) {
58 | $this->login = (string) $login;
59 | }
60 |
61 | /**
62 | * @return Collection
63 | */
64 | public function getComments() {
65 | return $this->comments;
66 | }
67 |
68 | /**
69 | * @param Comment $comment
70 | */
71 | public function addComment(Comment $comment) {
72 | $this->comments->add($comment);
73 | $comment->setUser($this);
74 | }
75 |
76 | }
--------------------------------------------------------------------------------
/library/Entity/Comment.php:
--------------------------------------------------------------------------------
1 | setContent($content);
36 | }
37 |
38 | /**
39 | * @return int
40 | */
41 | public function getId() {
42 | return $this->id;
43 | }
44 |
45 | /**
46 | * @return string
47 | */
48 | public function getContent() {
49 | return $this->content;
50 | }
51 |
52 | /**
53 | * @param string $content
54 | */
55 | public function setContent($content) {
56 | $this->content = (string) $content;
57 | }
58 |
59 | /**
60 | * @return User|null
61 | */
62 | public function getUser() {
63 | return $this->user;
64 | }
65 |
66 | /**
67 | * @param User $user
68 | */
69 | public function setUser($user) {
70 | if($user === null || $user instanceof User) {
71 | $this->user = $user;
72 | } else {
73 | throw new InvalidArgumentException('$user must be instance of Entity\User or null!');
74 | }
75 | }
76 |
77 | }
--------------------------------------------------------------------------------
/js/loadhtmlslides.js:
--------------------------------------------------------------------------------
1 | // Modified from markdown.js from Hakim to handle external html files
2 | (function () {
3 | /*jslint loopfunc: true, browser: true*/
4 | /*globals alert*/
5 | 'use strict';
6 |
7 | var querySlidingHtml = function () {
8 | var sections = document.querySelectorAll('[data-html]'),
9 | section, j, jlen;
10 |
11 | for (j = 0, jlen = sections.length; j < jlen; j++) {
12 | section = sections[j];
13 |
14 | if (section.getAttribute('data-html').length) {
15 |
16 | var xhr = new XMLHttpRequest(),
17 | url = section.getAttribute('data-html'),
18 | cb = function () {
19 | var backgroundImage;
20 |
21 | if (xhr.readyState === 4) {
22 | if (xhr.status >= 200 && xhr.status < 300) {
23 | section.innerHTML = xhr.responseText;
24 | if (backgroundImage = section.querySelectorAll("img[data-background-image]").item(0)) {
25 | section.setAttribute(
26 | 'data-background-image',
27 | backgroundImage.getAttribute('src')
28 | );
29 |
30 | backgroundImage.parentNode.removeChild(backgroundImage);
31 | }
32 | } else {
33 | section.outerHTML = 'ERROR: The attempt to fetch ' + url + ' failed with the HTTP status ' + xhr.status + '. Check your browser\'s JavaScript console for more details. ';
34 | }
35 | }
36 | };
37 |
38 | xhr.onreadystatechange = cb;
39 |
40 | xhr.open('GET', url, false);
41 | try {
42 | xhr.send();
43 | } catch (e) {
44 | alert('Failed to get file' + url + '.' + e);
45 | }
46 | }
47 | }
48 | };
49 |
50 | querySlidingHtml();
51 | })();
52 |
--------------------------------------------------------------------------------
/assets/img/gh.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Created by potrace 1.10, written by Peter Selinger 2001-2011
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/assets/img/zf-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | image/svg+xml
--------------------------------------------------------------------------------
/css/source/theme.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Default theme for reveal.js.
3 | *
4 | * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
5 | */
6 | // This file has been copied over from
7 | // ../../bower_components/reveal.js/css/theme/source/default.scss
8 | // Default mixins and settings -----------------
9 | @import "../../bower_components/reveal.js/css/theme/template/mixins";
10 | @import "../../bower_components/reveal.js/css/theme/template/settings";
11 | //@import "sky";
12 |
13 | // ---------------------------------------------
14 |
15 | // Include theme-specific fonts ----------------
16 | @font-face {
17 | font-family: 'League Gothic';
18 | src: url('../../lib/font/league_gothic-webfont.eot');
19 | src: url('../../lib/font/league_gothic-webfont.eot?#iefix') format('embedded-opentype'), url('../../lib/font/league_gothic-webfont.woff') format('woff'), url('../../lib/font/league_gothic-webfont.ttf') format('truetype'), url('../../lib/font/league_gothic-webfont.svg#LeagueGothicRegular') format('svg');
20 |
21 | font-weight: normal;
22 | font-style: normal;
23 | }
24 |
25 | @import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
26 | // ---------------------------------------------
27 |
28 | // Override theme settings ---------------------
29 | $heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, .1), 0 0 5px rgba(0, 0, 0, .1), 0 1px 3px rgba(0, 0, 0, .3), 0 3px 5px rgba(0, 0, 0, .2), 0 5px 10px rgba(0, 0, 0, .25), 0 20px 20px rgba(0, 0, 0, .15);
30 | // Other options include e.g.
31 | // $mainFont: 'Open Sans', sans-serif;
32 | // $linkColor: #ed1dff;
33 | // $linkColorHover: $linkColor;
34 | // $headingFont: 'Montserrat', Impact, sans-serif;
35 | // $headingTextShadow: none;
36 | // $headingLetterSpacing: -0.03em;
37 | // $headingTextTransform: none;
38 | // $selectionBackgroundColor: #e0ad52;
39 | // $mainFontSize: 30px;
40 | // See ../../bower_components/reveal.js/css/theme/template/settings.scss for the full list.
41 | // ---------------------------------------------
42 |
43 | // Background generator ------------------------
44 | //@mixin bodyBackground() {
45 | // @include radial-gradient(rgba(28, 30, 32, 1), rgba(85, 90, 95, 1));
46 | //}
47 |
48 | // ---------------------------------------------
49 |
50 | // Theme template ------------------------------
51 | @import "../../bower_components/reveal.js/css/theme/template/theme";
52 | @import "../../bower_components/reveal.js/css/theme/source/sky";
53 | // ---------------------------------------------
54 |
55 | // See ../../bower_components/reveal.js/css/theme/README.md
56 | // for further explanations on how reveal.js themes work.
57 |
58 | // Custom Styles
59 | img.no-border {
60 | border: none !important;
61 | background: none !important;
62 | box-shadow: none !important;
63 | }
64 |
65 | div.half {
66 | width: 50%;
67 | float: left;
68 | }
69 |
70 | div.clear {
71 | clear:both;
72 | }
73 |
74 | footer.tiny {
75 | font-size: medium;
76 | }
77 |
78 | .left {
79 | text-align: left;
80 | }
81 |
82 | code {
83 | color: orangered;
84 | }
85 |
86 | .zf-color {
87 | color: #68B604;
88 | }
89 |
90 | .doctrine-color {
91 | color: #FC6A31;
92 | }
93 |
94 | .doctrine-bg {
95 | background-color: #040E27;
96 | }
97 |
98 | .reveal section img {
99 | border: 0;
100 | background: none;
101 | box-shadow: 0 0 #000;
102 | }
103 |
104 | .reveal pre {
105 | width: 100%;
106 | font-size: 0.68em;
107 | }
108 |
109 | .reveal pre code {
110 | max-height: 600px;
111 | }
--------------------------------------------------------------------------------
/templates/_index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Doctrine best pratices
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
21 |
22 |
23 |
24 |
25 | <% _.forEach(slides, function(slide) { %>
26 | <% if (!_.isArray(slide)) { %>
27 | <%= section(slide) %>
28 | <% } %>
29 | <% if (_.isArray(slide)) { %>
30 |
31 | <% _.forEach(slide, function(verticalslide) { %>
32 | <%= section(verticalslide) %>
33 | <% }); %>
34 |
35 | <% } %>
36 | <% }); %>
37 |
38 |
39 |
40 |
41 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/Gruntfile.coffee:
--------------------------------------------------------------------------------
1 | # Generated on 2014-05-17 using generator-reveal 0.3.4
2 | module.exports = (grunt) ->
3 |
4 | grunt.initConfig
5 |
6 | watch:
7 |
8 | livereload:
9 | options:
10 | livereload: true
11 | files: [
12 | 'index.html'
13 | 'slides/**/*.md'
14 | 'slides/**/*.html'
15 | 'slides/***.html'
16 | 'js/*.js'
17 | 'css/*.css'
18 | ]
19 |
20 | index:
21 | files: [
22 | 'templates/_index.html'
23 | 'templates/_section.html'
24 | 'slides/list.json'
25 | 'slides/**.html'
26 | ]
27 | tasks: ['buildIndex']
28 |
29 | coffeelint:
30 | files: ['Gruntfile.coffee']
31 | tasks: ['coffeelint']
32 |
33 | jshint:
34 | files: ['js/*.js']
35 | tasks: ['jshint']
36 |
37 | sass:
38 | files: ['css/source/theme.scss']
39 | tasks: ['sass']
40 |
41 | sass:
42 |
43 | theme:
44 | files:
45 | 'css/theme.css': 'css/source/theme.scss'
46 |
47 | connect:
48 |
49 | livereload:
50 | options:
51 | port: 9001
52 | # Change hostname to '0.0.0.0' to access
53 | # the server from outside.
54 | hostname: '0.0.0.0'
55 | base: '.'
56 | open: true
57 | livereload: true
58 |
59 | coffeelint:
60 |
61 | options:
62 | indentation:
63 | value: 4
64 |
65 | all: ['Gruntfile.coffee']
66 |
67 | jshint:
68 |
69 | options:
70 | jshintrc: '.jshintrc'
71 |
72 | all: ['js/*.js']
73 |
74 | copy:
75 |
76 | dist:
77 | files: [{
78 | expand: true
79 | src: [
80 | 'slides/**'
81 | 'bower_components/**'
82 | 'js/**'
83 | 'css/*.css'
84 | ]
85 | dest: 'dist/'
86 | },{
87 | expand: true
88 | src: ['index.html']
89 | dest: 'dist/'
90 | filter: 'isFile'
91 | }]
92 |
93 |
94 | # Load all grunt tasks.
95 | require('load-grunt-tasks')(grunt)
96 |
97 | grunt.registerTask 'buildIndex',
98 | 'Build index.html from templates/_index.html and slides/list.json.',
99 | ->
100 | indexTemplate = grunt.file.read 'templates/_index.html'
101 | sectionTemplate = grunt.file.read 'templates/_section.html'
102 | slides = grunt.file.readJSON 'slides/list.json'
103 |
104 | html = grunt.template.process indexTemplate, data:
105 | slides:
106 | slides
107 | section: (slide) ->
108 | grunt.template.process sectionTemplate, data:
109 | slide:
110 | slide
111 | grunt.file.write 'index.html', html
112 |
113 | grunt.registerTask 'test',
114 | '*Lint* javascript and coffee files.', [
115 | 'coffeelint'
116 | 'jshint'
117 | ]
118 |
119 | grunt.registerTask 'server',
120 | 'Run presentation locally and start watch process (living document).', [
121 | 'buildIndex'
122 | 'sass'
123 | 'connect:livereload'
124 | 'watch'
125 | ]
126 |
127 | grunt.registerTask 'dist',
128 | 'Save presentation files to *dist* directory.', [
129 | 'test'
130 | 'sass'
131 | 'buildIndex'
132 | 'copy'
133 | ]
134 |
135 | # Define default task.
136 | grunt.registerTask 'default', [
137 | 'test'
138 | 'server'
139 | ]
140 |
--------------------------------------------------------------------------------
/assets/font/specimen_files/grid_12-825-55-15.css:
--------------------------------------------------------------------------------
1 | /*Notes about grid:
2 | Columns: 12
3 | Grid Width: 825px
4 | Column Width: 55px
5 | Gutter Width: 15px
6 | -------------------------------*/
7 |
8 |
9 |
10 | .section {margin-bottom: 18px;
11 | }
12 | .section:after {content: ".";display: block;height: 0;clear: both;visibility: hidden;}
13 | .section {*zoom: 1;}
14 |
15 | .section .firstcolumn,
16 | .section .firstcol {margin-left: 0;}
17 |
18 |
19 | /* Border on left hand side of a column. */
20 | .border {
21 | padding-left: 7px;
22 | margin-left: 7px;
23 | border-left: 1px solid #eee;
24 | }
25 |
26 | /* Border with more whitespace, spans one column. */
27 | .colborder {
28 | padding-left: 42px;
29 | margin-left: 42px;
30 | border-left: 1px solid #eee;
31 | }
32 |
33 |
34 |
35 | /* The Grid Classes */
36 | .grid1, .grid1_2cols, .grid1_3cols, .grid1_4cols, .grid2, .grid2_3cols, .grid2_4cols, .grid3, .grid3_2cols, .grid3_4cols, .grid4, .grid4_3cols, .grid5, .grid5_2cols, .grid5_3cols, .grid5_4cols, .grid6, .grid6_4cols, .grid7, .grid7_2cols, .grid7_3cols, .grid7_4cols, .grid8, .grid8_3cols, .grid9, .grid9_2cols, .grid9_4cols, .grid10, .grid10_3cols, .grid10_4cols, .grid11, .grid11_2cols, .grid11_3cols, .grid11_4cols, .grid12
37 | {margin-left: 15px;float: left;display: inline; overflow: hidden;}
38 |
39 |
40 | .width1, .grid1, .span-1 {width: 55px;}
41 | .width1_2cols,.grid1_2cols {width: 20px;}
42 | .width1_3cols,.grid1_3cols {width: 8px;}
43 | .width1_4cols,.grid1_4cols {width: 2px;}
44 | .input_width1 {width: 49px;}
45 |
46 | .width2, .grid2, .span-2 {width: 125px;}
47 | .width2_3cols,.grid2_3cols {width: 31px;}
48 | .width2_4cols,.grid2_4cols {width: 20px;}
49 | .input_width2 {width: 119px;}
50 |
51 | .width3, .grid3, .span-3 {width: 195px;}
52 | .width3_2cols,.grid3_2cols {width: 90px;}
53 | .width3_4cols,.grid3_4cols {width: 37px;}
54 | .input_width3 {width: 189px;}
55 |
56 | .width4, .grid4, .span-4 {width: 265px;}
57 | .width4_3cols,.grid4_3cols {width: 78px;}
58 | .input_width4 {width: 259px;}
59 |
60 | .width5, .grid5, .span-5 {width: 335px;}
61 | .width5_2cols,.grid5_2cols {width: 160px;}
62 | .width5_3cols,.grid5_3cols {width: 101px;}
63 | .width5_4cols,.grid5_4cols {width: 72px;}
64 | .input_width5 {width: 329px;}
65 |
66 | .width6, .grid6, .span-6 {width: 405px;}
67 | .width6_4cols,.grid6_4cols {width: 90px;}
68 | .input_width6 {width: 399px;}
69 |
70 | .width7, .grid7, .span-7 {width: 475px;}
71 | .width7_2cols,.grid7_2cols {width: 230px;}
72 | .width7_3cols,.grid7_3cols {width: 148px;}
73 | .width7_4cols,.grid7_4cols {width: 107px;}
74 | .input_width7 {width: 469px;}
75 |
76 | .width8, .grid8, .span-8 {width: 545px;}
77 | .width8_3cols,.grid8_3cols {width: 171px;}
78 | .input_width8 {width: 539px;}
79 |
80 | .width9, .grid9, .span-9 {width: 615px;}
81 | .width9_2cols,.grid9_2cols {width: 300px;}
82 | .width9_4cols,.grid9_4cols {width: 142px;}
83 | .input_width9 {width: 609px;}
84 |
85 | .width10, .grid10, .span-10 {width: 685px;}
86 | .width10_3cols,.grid10_3cols {width: 218px;}
87 | .width10_4cols,.grid10_4cols {width: 160px;}
88 | .input_width10 {width: 679px;}
89 |
90 | .width11, .grid11, .span-11 {width: 755px;}
91 | .width11_2cols,.grid11_2cols {width: 370px;}
92 | .width11_3cols,.grid11_3cols {width: 241px;}
93 | .width11_4cols,.grid11_4cols {width: 177px;}
94 | .input_width11 {width: 749px;}
95 |
96 | .width12, .grid12, .span-12 {width: 825px;}
97 | .input_width12 {width: 819px;}
98 |
99 | /* Subdivided grid spaces */
100 | .emptycols_left1, .prepend-1 {padding-left: 70px;}
101 | .emptycols_right1, .append-1 {padding-right: 70px;}
102 | .emptycols_left2, .prepend-2 {padding-left: 140px;}
103 | .emptycols_right2, .append-2 {padding-right: 140px;}
104 | .emptycols_left3, .prepend-3 {padding-left: 210px;}
105 | .emptycols_right3, .append-3 {padding-right: 210px;}
106 | .emptycols_left4, .prepend-4 {padding-left: 280px;}
107 | .emptycols_right4, .append-4 {padding-right: 280px;}
108 | .emptycols_left5, .prepend-5 {padding-left: 350px;}
109 | .emptycols_right5, .append-5 {padding-right: 350px;}
110 | .emptycols_left6, .prepend-6 {padding-left: 420px;}
111 | .emptycols_right6, .append-6 {padding-right: 420px;}
112 | .emptycols_left7, .prepend-7 {padding-left: 490px;}
113 | .emptycols_right7, .append-7 {padding-right: 490px;}
114 | .emptycols_left8, .prepend-8 {padding-left: 560px;}
115 | .emptycols_right8, .append-8 {padding-right: 560px;}
116 | .emptycols_left9, .prepend-9 {padding-left: 630px;}
117 | .emptycols_right9, .append-9 {padding-right: 630px;}
118 | .emptycols_left10, .prepend-10 {padding-left: 700px;}
119 | .emptycols_right10, .append-10 {padding-right: 700px;}
120 | .emptycols_left11, .prepend-11 {padding-left: 770px;}
121 | .emptycols_right11, .append-11 {padding-right: 770px;}
122 | .pull-1 {margin-left: -70px;}
123 | .push-1 {margin-right: -70px;margin-left: 18px;float: right;}
124 | .pull-2 {margin-left: -140px;}
125 | .push-2 {margin-right: -140px;margin-left: 18px;float: right;}
126 | .pull-3 {margin-left: -210px;}
127 | .push-3 {margin-right: -210px;margin-left: 18px;float: right;}
128 | .pull-4 {margin-left: -280px;}
129 | .push-4 {margin-right: -280px;margin-left: 18px;float: right;}
--------------------------------------------------------------------------------
/assets/img/doctrine.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
20 |
44 |
48 |
52 |
53 |
55 |
56 |
58 | image/svg+xml
59 |
61 |
62 |
63 |
64 |
65 |
71 |
84 |
90 |
103 |
104 |
109 |
115 |
128 |
141 |
154 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/assets/img/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | image/svg+xml
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/assets/img/roave.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/assets/font/specimen_files/specimen_stylesheet.css:
--------------------------------------------------------------------------------
1 | @import url('grid_12-825-55-15.css');
2 |
3 | /*
4 | CSS Reset by Eric Meyer - Released under Public Domain
5 | http://meyerweb.com/eric/tools/css/reset/
6 | */
7 | html, body, div, span, applet, object, iframe,
8 | h1, h2, h3, h4, h5, h6, p, blockquote, pre,
9 | a, abbr, acronym, address, big, cite, code,
10 | del, dfn, em, font, img, ins, kbd, q, s, samp,
11 | small, strike, strong, sub, sup, tt, var,
12 | b, u, i, center, dl, dt, dd, ol, ul, li,
13 | fieldset, form, label, legend, table,
14 | caption, tbody, tfoot, thead, tr, th, td
15 | {margin: 0;padding: 0;border: 0;outline: 0;
16 | font-size: 100%;vertical-align: baseline;
17 | background: transparent;}
18 | body {line-height: 1;}
19 | ol, ul {list-style: none;}
20 | blockquote, q {quotes: none;}
21 | blockquote:before, blockquote:after,
22 | q:before, q:after {content: ''; content: none;}
23 | :focus {outline: 0;}
24 | ins {text-decoration: none;}
25 | del {text-decoration: line-through;}
26 | table {border-collapse: collapse;border-spacing: 0;}
27 |
28 |
29 |
30 |
31 | body {
32 | color: #000;
33 | background-color: #dcdcdc;
34 | }
35 |
36 | a {
37 | text-decoration: none;
38 | color: #1883ba;
39 | }
40 |
41 | h1{
42 | font-size: 32px;
43 | font-weight: normal;
44 | font-style: normal;
45 | margin-bottom: 18px;
46 | }
47 |
48 | h2{
49 | font-size: 18px;
50 | }
51 |
52 | #container {
53 | width: 865px;
54 | margin: 0px auto;
55 | }
56 |
57 |
58 | #header {
59 | padding: 20px;
60 | font-size: 36px;
61 | background-color: #000;
62 | color: #fff;
63 | }
64 |
65 | #header span {
66 | color: #666;
67 | }
68 | #main_content {
69 | background-color: #fff;
70 | padding: 60px 20px 20px;
71 | }
72 |
73 |
74 | #footer p {
75 | margin: 0;
76 | padding-top: 10px;
77 | padding-bottom: 50px;
78 | color: #333;
79 | font: 10px Arial, sans-serif;
80 | }
81 |
82 | .tabs {
83 | width: 100%;
84 | height: 31px;
85 | background-color: #444;
86 | }
87 | .tabs li {
88 | float: left;
89 | margin: 0;
90 | overflow: hidden;
91 | background-color: #444;
92 | }
93 | .tabs li a {
94 | display: block;
95 | color: #fff;
96 | text-decoration: none;
97 | font: bold 11px/11px 'Arial';
98 | text-transform: uppercase;
99 | padding: 10px 15px;
100 | border-right: 1px solid #fff;
101 | }
102 |
103 | .tabs li a:hover {
104 | background-color: #00b3ff;
105 |
106 | }
107 |
108 | .tabs li.active a {
109 | color: #000;
110 | background-color: #fff;
111 | }
112 |
113 |
114 |
115 | div.huge {
116 |
117 | font-size: 300px;
118 | line-height: 1em;
119 | padding: 0;
120 | letter-spacing: -.02em;
121 | overflow: hidden;
122 | }
123 | div.glyph_range {
124 | font-size: 72px;
125 | line-height: 1.1em;
126 | }
127 |
128 | .size10{ font-size: 10px; }
129 | .size11{ font-size: 11px; }
130 | .size12{ font-size: 12px; }
131 | .size13{ font-size: 13px; }
132 | .size14{ font-size: 14px; }
133 | .size16{ font-size: 16px; }
134 | .size18{ font-size: 18px; }
135 | .size20{ font-size: 20px; }
136 | .size24{ font-size: 24px; }
137 | .size30{ font-size: 30px; }
138 | .size36{ font-size: 36px; }
139 | .size48{ font-size: 48px; }
140 | .size60{ font-size: 60px; }
141 | .size72{ font-size: 72px; }
142 | .size90{ font-size: 90px; }
143 |
144 |
145 | .psample_row1 { height: 120px;}
146 | .psample_row1 { height: 120px;}
147 | .psample_row2 { height: 160px;}
148 | .psample_row3 { height: 160px;}
149 | .psample_row4 { height: 160px;}
150 |
151 | .psample {
152 | overflow: hidden;
153 | position: relative;
154 | }
155 | .psample p {
156 | line-height: 1.3em;
157 | display: block;
158 | overflow: hidden;
159 | margin: 0;
160 | }
161 |
162 | .psample span {
163 | margin-right: .5em;
164 | }
165 |
166 | .white_blend {
167 | width: 100%;
168 | height: 61px;
169 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAO1JREFUeNrs3TsKgFAMRUE/eer+NxztxMYuEWQG3ECKwwUF58ycAKixOAGAyAKILAAiCyCyACILgMgCiCyAyAIgsgAiCyCyAIgsgMgCiCwAIgsgsgAiC4DIAogsACIL0CWuZ3UGgLrIhjMA1EV2OAOAJQtgyQLwjOzmDAAiCyCyAIgsQFtkd2cAEFkAkQVAZAHaIns4A4AlC2DJAiCyACILILIAiCzAV5H1dQGAJQsgsgCILIDIAvwisl58AViyAJYsACILILIAIgvAe2T9EhxAZAFEFgCRBeiL7HAGgLrIhjMAWLIAliwAt1OAAQDwygTBulLIlQAAAABJRU5ErkJggg==);
170 | position: absolute;
171 | bottom: 0;
172 | }
173 | .black_blend {
174 | width: 100%;
175 | height: 61px;
176 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVkAAAA9CAYAAAAH4BojAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAPJJREFUeNrs3TEKhTAQRVGjibr/9QoxhY2N3Ywo50A28IrLwP9g6b1PAMSYTQAgsgAiC4DIAogsgMgCILIAIgsgsgCILIDIAogsACILILIAIguAyAKILIDIAiCyACILgMgCZCnjLWYAiFGvB0BQZJsZAFyyAC5ZAO6RXc0AILIAIguAyAKkRXYzA4DIAogsACILkBbZ3QwALlkAlywAIgsgsgAiC4DIArwVWf8uAHDJAogsACILILIAv4isH74AXLIALlkARBZAZAFEFoDnyPokOIDIAogsACILkBfZZgaAuMhWMwC4ZAE+p4x3mAEgxinAAJ+XBbPWGkwAAAAAAElFTkSuQmCC);
177 | position: absolute;
178 | bottom: 0;
179 | }
180 | .fullreverse {
181 | background: #000 !important;
182 | color: #fff !important;
183 | margin-left: -20px;
184 | padding-left: 20px;
185 | margin-right: -20px;
186 | padding-right: 20px;
187 | padding: 20px;
188 | margin-bottom:0;
189 | }
190 |
191 |
192 | .sample_table td {
193 | padding-top: 3px;
194 | padding-bottom:5px;
195 | padding-left: 5px;
196 | vertical-align: middle;
197 | line-height: 1.2em;
198 | }
199 |
200 | .sample_table td:first-child {
201 | background-color: #eee;
202 | text-align: right;
203 | padding-right: 5px;
204 | padding-left: 0;
205 | padding: 5px;
206 | font: 11px/12px "Courier New", Courier, mono;
207 | }
208 |
209 | code {
210 | white-space: pre;
211 | background-color: #eee;
212 | display: block;
213 | padding: 10px;
214 | margin-bottom: 18px;
215 | overflow: auto;
216 | }
217 |
218 |
219 | .bottom,.last {margin-bottom:0 !important; padding-bottom:0 !important;}
220 |
221 | .box {
222 | padding: 18px;
223 | margin-bottom: 18px;
224 | background: #eee;
225 | }
226 |
227 | .reverse,.reversed { background: #000 !important;color: #fff !important; border: none !important;}
228 |
229 | #bodycomparison {
230 | position: relative;
231 | overflow: hidden;
232 | font-size: 72px;
233 | height: 90px;
234 | white-space: nowrap;
235 | }
236 |
237 | #bodycomparison div{
238 | font-size: 72px;
239 | line-height: 90px;
240 | display: inline;
241 | margin: 0 15px 0 0;
242 | padding: 0;
243 | }
244 |
245 | #bodycomparison div span{
246 | font: 10px Arial;
247 | position: absolute;
248 | left: 0;
249 | }
250 | #xheight {
251 | float: none;
252 | position: absolute;
253 | color: #d9f3ff;
254 | font-size: 72px;
255 | line-height: 90px;
256 | }
257 |
258 | .fontbody {
259 | position: relative;
260 | }
261 | .arialbody{
262 | font-family: Arial;
263 | position: relative;
264 | }
265 | .verdanabody{
266 | font-family: Verdana;
267 | position: relative;
268 | }
269 | .georgiabody{
270 | font-family: Georgia;
271 | position: relative;
272 | }
273 |
274 | /* @group Layout page
275 | */
276 |
277 | #layout h1 {
278 | font-size: 36px;
279 | line-height: 42px;
280 | font-weight: normal;
281 | font-style: normal;
282 | }
283 |
284 | #layout h2 {
285 | font-size: 24px;
286 | line-height: 23px;
287 | font-weight: normal;
288 | font-style: normal;
289 | }
290 |
291 | #layout h3 {
292 | font-size: 22px;
293 | line-height: 1.4em;
294 | margin-top: 1em;
295 | font-weight: normal;
296 | font-style: normal;
297 | }
298 |
299 |
300 | #layout p.byline {
301 | font-size: 12px;
302 | margin-top: 18px;
303 | line-height: 12px;
304 | margin-bottom: 0;
305 | }
306 | #layout p {
307 | font-size: 14px;
308 | line-height: 21px;
309 | margin-bottom: .5em;
310 | }
311 |
312 | #layout p.large{
313 | font-size: 18px;
314 | line-height: 26px;
315 | }
316 |
317 | #layout .sidebar p{
318 | font-size: 12px;
319 | line-height: 1.4em;
320 | }
321 |
322 | #layout p.caption {
323 | font-size: 10px;
324 | margin-top: -16px;
325 | margin-bottom: 18px;
326 | }
327 |
328 | /* @end */
329 |
330 | /* @group Glyphs */
331 |
332 | #glyph_chart div{
333 | background-color: #d9f3ff;
334 | color: black;
335 | float: left;
336 | font-size: 36px;
337 | height: 1.2em;
338 | line-height: 1.2em;
339 | margin-bottom: 1px;
340 | margin-right: 1px;
341 | text-align: center;
342 | width: 1.2em;
343 | position: relative;
344 | padding: .6em .2em .2em;
345 | }
346 |
347 | #glyph_chart div p {
348 | position: absolute;
349 | left: 0;
350 | top: 0;
351 | display: block;
352 | text-align: center;
353 | font: bold 9px Arial, sans-serif;
354 | background-color: #3a768f;
355 | width: 100%;
356 | color: #fff;
357 | padding: 2px 0;
358 | }
359 |
360 |
361 | #glyphs h1 {
362 | font-family: Arial, sans-serif;
363 | }
364 | /* @end */
365 |
366 | /* @group Installing */
367 |
368 | #installing {
369 | font: 13px Arial, sans-serif;
370 | }
371 |
372 | #installing p,
373 | #glyphs p{
374 | line-height: 1.2em;
375 | margin-bottom: 18px;
376 | font: 13px Arial, sans-serif;
377 | }
378 |
379 |
380 |
381 | #installing h3{
382 | font-size: 15px;
383 | margin-top: 18px;
384 | }
385 |
386 | /* @end */
387 |
388 | #rendering h1 {
389 | font-family: Arial, sans-serif;
390 | }
391 | .render_table td {
392 | font: 11px "Courier New", Courier, mono;
393 | vertical-align: middle;
394 | }
395 |
396 |
397 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Doctrine 2 ORM Tutorial
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
Doctrine 2 ORM
68 |
Object Relational Mapper for PHP 5.3
69 |
70 |
71 |
72 |
73 |
74 |
Some history of RDBMS persistence in PHP:
75 |
76 | mysql_query, mysql_fetch_array, mysql_*
77 | mysqli, PDO
78 | Doctrine 1.2, Propel 1.6, Zend_Db
79 |
80 |
81 |
82 |
mysql_query
83 |
84 |
85 |
86 | mysql_connect('localhost', 'user', 'password');
87 | mysql_select_db('test');
88 |
89 | //simple example
90 | mysql_query('INSERT INTO greetings (content) VALUES ("' . mysql_real_escape_string($_POST['greeting']) . '")');
91 |
92 | //comments example
93 | mysql_query('INSERT INTO users (username) VALUES ("' . mysql_real_escape_string($_POST['username']) . '")');
94 | mysql_query('INSERT INTO comments (user_id, content) VALUES (' . mysql_insert_id() . ', "' . mysql_real_escape_string($_POST['content']) . '")');
95 |
96 |
97 |
98 |
mysql_query
99 |
100 |
101 | We had to write our queries and our security (mysql_real_escape_string()) by ourselves.
102 |
103 |
104 | Also, we had to keep track of identifiers of the inserted fields by ourselves.
105 |
106 |
107 | We had to rewrite every function name (and probably also functionality) when switching database vendor.
108 |
109 |
110 | Not really OO friendly, heh?
111 |
112 |
113 |
114 |
115 |
PDO
116 |
117 |
118 |
119 | try {
120 | $dbh = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
121 |
122 | //simple example
123 | $sth = $dbh->prepare('INSERT INTO greetings (content) VALUES (:content);
124 | $sth->execute(array(':content' => $_POST['content']));
125 |
126 | //comments example
127 | $sth = $dbh->prepare('INSERT INTO users (username) VALUES (:username)');
128 | $sth->execute(array(':username' => $_POST['username']));
129 | $sth = $dbh->prepare('INSERT INTO comments (user_id, content) VALUES (user_id, :content)');
130 | $sth->execute(array(':user_id' => $dbh->lastInsertId(), ':content' => $_POST['content']);
131 |
132 | } catch (PDOException $e) {
133 | echo $e->getMessage();
134 | }
135 |
136 |
137 |
138 |
PDO
139 |
140 |
141 | We now can connect to different databases. MySQL is no more the only option!
142 |
143 |
144 | Security is given by named parameters, escaped for us by PDO!
145 |
146 |
147 | Prepared statements make our queries easier to reuse!
148 |
149 |
150 | Exceptions are used, we can now catch them!
151 |
152 |
153 | Queries are still performed in the SQL dialect of the vendor, which could make our code not 100% portable.
154 |
155 |
156 | Still have to keep track of identifiers of the inserted fields by ourselves.
157 |
158 |
159 |
160 |
161 |
Doctrine 1.2
162 |
An Active Record implementation
163 |
164 |
165 |
Greeting record definition
166 |
167 | class Greeting extends Doctrine_Record
168 | {
169 | public function setTableDefinition()
170 | {
171 | $this->setTableName('greetings');
172 | $this->hasColumn(
173 | 'content',
174 | 'string',
175 | 255,
176 | array(
177 | 'type' => 'string',
178 | 'length' => '255'
179 | )
180 | );
181 | }
182 |
183 | }
184 |
185 |
186 |
187 |
188 | //simple example
189 | $greeting = new Greeting();
190 | $greeting->content = $_POST['content'];
191 | $greeting->save();
192 |
193 |
194 |
195 |
User record definition
196 |
197 | class User extends Doctrine_Record
198 | {
199 | public function setTableDefinition()
200 | {
201 | $this->setTableName('users');
202 | $this->hasColumn(
203 | 'username',
204 | 'string',
205 | 255,
206 | array(
207 | 'type' => 'text',
208 | )
209 | );
210 | }
211 |
212 | public function setUp()
213 | {
214 | $this->hasMany(
215 | 'Comments as Comments',
216 | array(
217 | 'refClass' => 'Comment',
218 | 'local' => 'id',
219 | 'foreign' => 'user_id'
220 | )
221 | );
222 | }
223 | }
224 |
225 |
226 |
227 |
Person record definition
228 |
229 | class Comment extends Doctrine_Record
230 | {
231 | public function setTableDefinition()
232 | {
233 | $this->setTableName('comments');
234 | $this->hasColumn(
235 | 'content',
236 | 'string',
237 | 255,
238 | array(
239 | 'type' => 'string',
240 | 'length' => '255'
241 | )
242 | );
243 | $this->hasColumn(
244 | 'user_id',
245 | 'string',
246 | 255,
247 | array(
248 | 'type' => 'integer',
249 | )
250 | );
251 | }
252 |
253 | public function setUp()
254 | {
255 | $this->hasOne(
256 | 'User',
257 | array(
258 | 'refClass' => 'User',
259 | 'local' => 'user_id',
260 | 'foreign' => 'id'
261 | )
262 | );
263 | }
264 | }
265 |
266 |
267 |
268 |
269 | //comments example
270 | $user = new User();
271 | $user->username = $_POST['username'];
272 | $comment = new Comment();
273 | $comment->content = $_POST['content'];
274 | $user->Comments[] = $comment;
275 | $comment->User = $user;
276 | $user->save();
277 |
278 |
279 |
280 |
Doctrine 1.2
281 |
282 |
283 | Objects are written to DB by Doctrine: no more SQL!
284 |
285 |
286 | Associations are managed by Doctrine: no more foreign key mess!
287 |
288 |
289 | Object-Oriented approach!
290 |
291 |
292 | Database is abstracted by a powerful DBAL layer: no more SQL portability troubles!
293 |
294 |
295 | Database can be generated by our models!
296 |
297 |
298 | Table definition is set in our domain logic.
299 |
300 |
301 | A lot of PHP "magic" is happening (__set, __get, etc.).
302 |
303 |
304 | Our objects must extend Doctrine_Record to be used with Doctrine
305 |
306 |
307 |
308 |
309 |
Doctrine 2
310 |
311 |
312 |
What is Doctrine 2?
313 |
314 |
315 | Doctrine 2 is a Data Mapper implementation:
316 |
317 |
318 |
319 |
320 | Used the well known Java Persistence API (JPA, aka JSR 317) as initial blueprint
321 |
322 |
323 | It separates persistence and domain logic. You are now able to port your logic to SQL, NoSQL, XML mappers, etc.
324 |
325 |
326 | It provides around 3X better performance than Doctrine 1.2.
327 |
328 |
329 | Your objects won't need to extend anything.
330 |
331 |
332 | You are free to save anything you want to your database, just map it!
333 |
334 |
335 | You can even map existing classes to database tables!
336 |
337 |
338 |
339 |
340 |
Doctrine 2 Requirements
341 |
342 |
343 | PHP 5.3
344 |
345 |
346 | Doctrine\Common (bundled)
347 |
348 | A set of utilities common to all Doctrine projects
349 |
350 |
351 | Doctrine\DBAL (bundled)
352 |
353 | A very powerful Database ABstraction Layer
354 |
355 |
356 | Symfony\Component\Console (bundled)
357 |
358 | A CLI runner tool
359 |
360 |
361 | Symfony\Component\Yaml (bundled)
362 |
363 | If you work with YAML configuration
364 |
365 |
366 |
367 |
368 |
Getting started
369 |
370 | mkdir doctrine-demo
371 | cd doctrine-demo
372 | mkdir library
373 | mkdir library/Entity
374 | touch library/Entity/Greeting.php
375 | mkdir library/EntityProxy
376 | chmod +w library/EntityProxy
377 | echo "{}" > composer.json
378 | curl -s https://getcomposer.org/installer | php
379 | ./composer.phar require doctrine/orm:2.*
380 | touch bootstrap.php
381 |
382 |
383 |
384 |
Create entities...
385 |
386 |
387 |
388 | namespace Entity;
389 |
390 | class Greeting
391 | {
392 | /** @var int */
393 | private $id;
394 |
395 | /** @var string */
396 | private $content;
397 |
398 | }
399 |
400 |
401 |
402 |
...add getters, setters, constructors, other methods...
403 |
404 |
405 |
406 | namespace Entity;
407 |
408 | class Greeting
409 | {
410 |
411 | /** @var int */
412 | private $id;
413 |
414 | /** @var string */
415 | private $content;
416 |
417 | public function __construct($content) {
418 | $this->setContent($content);
419 | }
420 |
421 | /**
422 | * @return int
423 | */
424 | public function getId() {
425 | return $this->id;
426 | }
427 |
428 | /**
429 | * @return string
430 | */
431 | public function getContent() {
432 | return $this->content;
433 | }
434 |
435 | /**
436 | * @param string $content
437 | */
438 | public function setContent($content) {
439 | $this->content = (string) $content;
440 | }
441 |
442 | }
443 |
444 |
445 |
446 |
...add mappings (@Annotations) to entities...
447 |
448 |
449 |
450 | namespace Entity;
451 |
452 | use Doctrine\ORM\Mapping as ORM;
453 |
454 | /**
455 | * @ORM\Entity
456 | */
457 | class Greeting
458 | {
459 |
460 | /**
461 | * @ORM\Id()
462 | * @ORM\Column(type="integer")
463 | * @ORM\GeneratedValue(strategy="AUTO")
464 | * @var int
465 | */
466 | private $id;
467 |
468 | /**
469 | * @ORM\Column(type="string", length=255)
470 | * @var string
471 | */
472 | private $content;
473 |
474 | public function __construct($content) {
475 | $this->setContent($content);
476 | }
477 |
478 | /**
479 | * @return int
480 | */
481 | public function getId() {
482 | return $this->id;
483 | }
484 |
485 | /**
486 | * @return string
487 | */
488 | public function getContent() {
489 | return $this->content;
490 | }
491 |
492 | /**
493 | * @param string $content
494 | */
495 | public function setContent($content) {
496 | $this->content = (string) $content;
497 | }
498 |
499 | }
500 |
501 |
502 |
503 |
Creating an EntityManager
504 |
505 |
506 |
Autoloading:
507 |
508 | <?php
509 | // bootstrap.php
510 | use Doctrine\ORM\Tools\Setup,
511 | Doctrine\ORM\EntityManager,
512 | Doctrine\ORM\Configuration,
513 | Doctrine\Common\Cache\ArrayCache as Cache,
514 | Doctrine\Common\Annotations\AnnotationRegistry,
515 | Doctrine\Common\ClassLoader;
516 |
517 | //autoloading
518 | require_once __DIR__ . '/vendor/autoload.php';
519 | $loader = new ClassLoader('Entity', __DIR__ . '/library');
520 | $loader->register();
521 | $loader = new ClassLoader('EntityProxy', __DIR__ . '/library');
522 | $loader->register();
523 |
524 |
525 |
526 |
Configuration and metadata
527 |
528 | //configuration
529 | $config = new Configuration();
530 | $cache = new Cache();
531 | $config->setQueryCacheImpl($cache);
532 | $config->setProxyDir(__DIR__ . '/library/EntityProxy');
533 | $config->setProxyNamespace('EntityProxy');
534 | $config->setAutoGenerateProxyClasses(true);
535 |
536 | //mapping (example uses annotations, could be any of XML/YAML or plain PHP)
537 | AnnotationRegistry::registerFile(__DIR__ . '/library/doctrine-orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
538 | $driver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver(
539 | new Doctrine\Common\Annotations\AnnotationReader(),
540 | array(__DIR__ . '/library/Entity')
541 | );
542 | $config->setMetadataDriverImpl($driver);
543 | $config->setMetadataCacheImpl($cache);
544 |
545 |
546 |
547 |
Creating the EntityManager
548 |
549 | //getting the EntityManager
550 | $em = EntityManager::create(
551 | array(
552 | 'driver' => 'pdo_sqlite',
553 | 'path' => 'database.sqlite'
554 | ),
555 | $config
556 | );
557 |
558 |
559 |
560 |
Final result:
561 |
562 | <?php
563 | // bootstrap.php
564 | use Doctrine\ORM\Tools\Setup,
565 | Doctrine\ORM\EntityManager,
566 | Doctrine\ORM\Configuration,
567 | Doctrine\Common\Cache\ArrayCache as Cache,
568 | Doctrine\Common\Annotations\AnnotationRegistry,
569 | Doctrine\Common\ClassLoader;
570 |
571 | //autoloading
572 | require_once __DIR__ . '/vendor/autoload.php';
573 | $loader = new ClassLoader('Entity', __DIR__ . '/library');
574 | $loader->register();
575 | $loader = new ClassLoader('EntityProxy', __DIR__ . '/library');
576 | $loader->register();
577 |
578 | //configuration
579 | $config = new Configuration();
580 | $cache = new Cache();
581 | $config->setQueryCacheImpl($cache);
582 | $config->setProxyDir(__DIR__ . '/library/EntityProxy');
583 | $config->setProxyNamespace('EntityProxy');
584 | $config->setAutoGenerateProxyClasses(true);
585 |
586 | //mapping (example uses annotations, could be any of XML/YAML or plain PHP)
587 | AnnotationRegistry::registerFile(__DIR__ . '/library/doctrine-orm/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php');
588 | $driver = new Doctrine\ORM\Mapping\Driver\AnnotationDriver(
589 | new Doctrine\Common\Annotations\AnnotationReader(),
590 | array(__DIR__ . '/library/Entity')
591 | );
592 | $config->setMetadataDriverImpl($driver);
593 | $config->setMetadataCacheImpl($cache);
594 |
595 | //getting the EntityManager
596 | $em = EntityManager::create(
597 | array(
598 | 'driver' => 'pdo_sqlite',
599 | 'path' => 'database.sqlite'
600 | ),
601 | $config
602 | );
603 |
604 |
605 |
606 |
Generating the schema
607 | (Configuring doctrine-cli.php)
608 |
609 |
610 |
CLI runner
611 |
612 | <?php
613 | //doctrine-cli.php
614 | use Symfony\Component\Console\Helper\HelperSet,
615 | Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper,
616 | Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper,
617 | Doctrine\ORM\Tools\Console\ConsoleRunner;
618 | require_once __DIR__ . '/bootstrap.php';
619 |
620 | $helperSet = new HelperSet(array(
621 | 'em' => new EntityManagerHelper($em),
622 | 'conn' => new ConnectionHelper($em->getConnection())
623 | ));
624 | ConsoleRunner::run($helperSet);
625 |
626 |
627 |
628 |
Generating the schema
629 |
630 | $ php doctrine-cli.php orm:schema-tool:create
631 | ATTENTION: This operation should not be executed in a production environment.
632 |
633 | Creating database schema...
634 | Database schema created successfully!
635 |
636 |
637 |
638 |
Generated database as seen by SQLite browser
639 |
640 |
641 |
642 |
Working with the EntityManager
643 |
644 |
645 |
Saving a "Entity\Greeting" object
646 |
647 | //examples/1.php
648 |
649 | use Entity\Greeting;
650 |
651 | require_once __DIR__ . '/../bootstrap.php';
652 |
653 | //Creating our greeting
654 | $greeting = new Greeting('Hello World!');
655 |
656 | //Registering $greeting with the EntityManager
657 | $em->persist($greeting);
658 |
659 | //Flushing all changes to database
660 | $em->flush();
661 |
662 | echo 'OK!';
663 |
664 |
665 |
666 |
Retrieving an "Entity\Greeting" object
667 |
668 | //examples/2.php
669 | require_once __DIR__ . '/../bootstrap.php';
670 |
671 | //Finding Greeting with id = 1
672 | $greeting = $em->find('Entity\Greeting', 1);
673 |
674 | if($greeting) {
675 | //The EntityManager has already provided us an object of type Entity\Greeting!
676 | echo 'Found a greeting (instance of ' . get_class($greeting)
677 | . ') with content ' . $greeting->getContent();
678 | }else{
679 | echo 'Couldn\'t find Greeting with id=1';
680 | }
681 |
682 |
683 |
684 |
Updating an "Entity\Greeting" object
685 |
686 | //examples/3.php
687 | require_once __DIR__ . '/../bootstrap.php';
688 |
689 | //Finding Greeting with id = 1
690 | $greeting = $em->find('Entity\Greeting', 1);
691 |
692 | if($greeting) {
693 | echo $greeting->getContent() . PHP_EOL;
694 | echo 'Changing the contents of found Greeting to "Hello Test!"' . PHP_EOL;
695 | //Using Entity\Greeting to set a new content for the $greeting!
696 | $greeting->setContent('Hello Test!');
697 | //Flushing changes to database (triggers SQL updates)
698 | $em->flush();
699 | echo 'Now try loading 2.php again!' . PHP_EOL;
700 | }else{
701 | echo 'Couldn\'t find Greeting with id=1';
702 | }
703 |
704 |
705 |
706 |
Finding "Entity\Greeting" objects
707 |
708 | //examples/4.php
709 | require_once __DIR__ . '/../bootstrap.php';
710 |
711 | //A repository is like a "Table" containing our entities of a specified type
712 | $repository = $em->getRepository('Entity\Greeting');
713 |
714 | //Finding all Entity\Greeting with content = "Hello World!"
715 | $worldGreetings = $repository->findBy(array('content' => 'Hello World!'));
716 |
717 | //Finding all Entity\Greeting with content = "Hello Test!"
718 | $testGreetings = $repository->findBy(array('content' => 'Hello Test!'));
719 |
720 | //Displaying results
721 | echo 'Found ' . count($worldGreetings) . ' "Hello World!" greetings:' . PHP_EOL;
722 | foreach($worldGreetings as $worldGreeting) {
723 | echo ' - ' . $worldGreeting->getId() . PHP_EOL;
724 | }
725 |
726 | echo 'Found ' . count($testGreetings) . ' "Hello Test!" greetings:' . PHP_EOL;
727 | foreach($testGreetings as $testGreeting) {
728 | echo ' - ' . $testGreeting->getId() . PHP_EOL;
729 | }
730 |
731 |
732 |
733 |
Retrieving "Entity\Greeting" objects via DQL
734 |
735 | //examples/5.php
736 | require_once __DIR__ . '/../bootstrap.php';
737 |
738 | //Creating a DQL query that selects all greetings with id >= 5 and id <= 10
739 | $greetings = $em
740 | ->createQuery('SELECT g FROM Entity\Greeting g WHERE g.id >= 5 AND g.id <= 10')
741 | ->getResult();
742 |
743 | //Displaying results
744 | echo 'Found ' . count($greetings) . ' Entity\Greeting:' . PHP_EOL;
745 | foreach($greetings as $greeting) {
746 | echo ' - ' . $greeting->getId() . ' => ' . $greeting->getContent() . PHP_EOL;
747 | }
748 |
749 |
750 |
751 |
Deleting "Entity\Greeting" objects
752 |
753 | //examples/6.php
754 | require_once __DIR__ . '/../bootstrap.php';
755 |
756 | //Finding the last inserted greeting
757 | $greetings = $em
758 | ->createQuery('SELECT g FROM Entity\Greeting g ORDER BY g.id DESC')
759 | ->setMaxResults(1) //we want only one result
760 | ->getResult();
761 |
762 | if(!empty($greetings)) {
763 | $greeting = reset($greetings);
764 | echo 'Found greeting with id "' . $greeting->getId()
765 | . '" and content "' . $greeting->getContent() . '"' . PHP_EOL;
766 | $em->remove($greeting);
767 | //Triggers delete
768 | $em->flush();
769 | echo 'Greeting deleted!' . PHP_EOL;
770 | } else {
771 | echo 'Could not find any Greeting' . PHP_EOL;
772 | }
773 |
774 |
775 |
776 |
Working with associations
777 |
778 |
779 |
You will need additional code:
780 |
781 | Entity\Post
782 | Entity\Comment
783 |
784 |
785 |
786 |
We don't define IDs, we define how objects are related:
787 |
788 | ONE Entity\Post
789 |
790 | HAS MANY Entity\Comment
791 |
792 |
793 | ONE Entity\Comment
794 |
795 | HAS ONE Entity\Post
796 |
797 |
798 |
799 |
In Doctrine terms:
800 |
801 | Entity\Post::comments
802 |
803 | is a OneToMany relation to Entity\Comment , mapped by Entity\Comment#post
804 |
805 |
806 | Entity\Comment::post
807 |
808 | is a ManyToOne relation to Entity\Post , inversed by Entity\Post#comments
809 |
810 |
811 |
812 |
Available relations:
813 |
814 |
815 | OneToMany (mapped by ManyToOne)
816 |
817 |
818 | ManyToOne (inversed by OneToMany)
819 |
820 |
821 | OneToOne (inversed/mapped by OneToOne)
822 |
823 |
824 | ManyToMany (inversed/mapped by ManyToMany)
825 |
826 |
827 |
828 | OneToMany and ManyToMany relations are represented in Doctrine by instances of the Doctrine\Common\Collections\Collection interface.
829 |
830 |
831 |
832 |
Relations ownership
833 |
834 | Relationship between entities may be inversed (bidirectional) or unidirectional .
835 |
836 |
837 |
838 |
839 | There is always an owning side of the relation.
840 |
841 |
842 | A bidirectional relationship has both an owning side and an inverse side .
843 |
844 |
845 | A unidirectional relationship has only an owning side .
846 |
847 |
848 | The owning side of the relation is the one checked by Doctrine to determine changes to the relation graph.
849 |
850 |
851 |
863 |
864 |
Using associations
865 | We will map some entities to check how associations work...
866 |
867 |
868 |
869 | /** @ORM\Entity */
870 | class User
871 | {
872 |
873 | /** @ORM\Id() @ORM\Column(type="integer") @ORM\GeneratedValue(strategy="AUTO") @var int */
874 | private $id;
875 |
876 | /** @ORM\Column(type="string", length=255) @var string */
877 | private $login;
878 |
879 | /**
880 | * @ORM\OneToMany(targetEntity="Entity\Comment", mappedBy="user")
881 | * @var Collection
882 | */
883 | private $comments;
884 |
885 | public function __construct($login) {
886 | //Initializing collection. Doctrine recognizes Collections, not arrays!
887 | $this->comments = new ArrayCollection();
888 | $this->setLogin($login);
889 | }
890 |
891 | //Getters and setters
892 |
893 | /** @return Collection */
894 | public function getComments() {
895 | return $this->comments;
896 | }
897 |
898 | /** @param Comment $comment */
899 | public function addComment(Comment $comment) {
900 | $this->comments->add($comment);
901 | $comment->setUser($this);
902 | }
903 |
904 | }
905 |
906 |
907 |
908 |
909 | /** @ORM\Entity */
910 | class Comment
911 | {
912 |
913 | /** @ORM\Id() @ORM\Column(type="integer") @ORM\GeneratedValue(strategy="AUTO") @var int */
914 | private $id;
915 |
916 | /** @ORM\Column(type="string", length=255) @var string */
917 | private $content;
918 |
919 | /**
920 | * @ORM\ManyToOne(targetEntity="Entity\User", inversedBy="comments")
921 | * @var User|null
922 | */
923 | private $user;
924 |
925 | public function __construct($content) {
926 | $this->setContent($content);
927 | }
928 |
929 | //Setters, getters
930 |
931 | /** @return User|null */
932 | public function getUser() {
933 | return $this->user;
934 | }
935 |
936 | /** @param User $user */
937 | public function setUser(User $user) {
938 | if($user === null || $user instanceof User) {
939 | $this->user = $user;
940 | } else {
941 | throw new InvalidArgumentException('$user must be instance of Entity\User or null!');
942 | }
943 | }
944 |
945 | }
946 |
947 |
948 |
949 |
Now to the examples
950 |
951 |
952 |
Creating a User with a related Comment
953 |
954 | //examples/7.php
955 | use Entity\User,
956 | Entity\Comment;
957 |
958 | require_once __DIR__ . '/../bootstrap.php';
959 |
960 | //Creating our user
961 | $user = new User('Marco Pivetta');
962 | $em->persist($user);
963 |
964 | $comment = new Comment('This is a sample post!');
965 | $em->persist($comment);
966 |
967 | $user->addComment($comment);
968 |
969 | //Flushing all changes to database
970 | $em->flush();
971 |
972 | echo 'OK!';
973 |
974 |
975 |
976 |
Fetching the User and it's related Comment
977 |
978 | //examples/8.php
979 | require_once __DIR__ . '/../bootstrap.php';
980 |
981 | //Finding previously persisted user
982 | $user = $em->find('Entity\User', 1);
983 |
984 | if($user) {
985 | echo 'Found an Entity\User: ' . PHP_EOL
986 | . $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
987 | . 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
988 | foreach($user->getComments() as $comment) {
989 | echo ' ' . $comment->getId() . ' => ' . $comment->getContent()
990 | . ' (' . get_class($comment) . ')' . PHP_EOL;
991 | }
992 | } else {
993 | echo 'Could not find Entity\User with id=1';
994 | }
995 |
996 |
997 |
998 |
Attaching a Comment to an existing User
999 |
1000 | //examples/9.php
1001 | use Entity\Comment;
1002 |
1003 | require_once __DIR__ . '/../bootstrap.php';
1004 |
1005 | //Finding previously persisted user
1006 | $user = $em->find('Entity\User', 1);
1007 |
1008 | if($user) {
1009 | echo 'Found an Entity\User: ' . PHP_EOL
1010 | . $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
1011 | . 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
1012 | echo 'Adding a Comment to the user';
1013 | $comment = new Comment('Comment generated at ' . time());
1014 | $em->persist($comment);
1015 | $user->addComment($comment);
1016 | $em->flush();
1017 | echo 'Comment has been attached to the user, try 8.php!';
1018 | } else {
1019 | echo 'Could not find Entity\User with id=1';
1020 | }
1021 |
1022 |
1023 |
1024 |
Removing a Comment attached to a User
1025 |
1026 | //examples/10.php
1027 | require_once __DIR__ . '/../bootstrap.php';
1028 |
1029 | $user = $em->find('Entity\User', 1);
1030 |
1031 | if($user) {
1032 | echo 'Found an Entity\User: ' . PHP_EOL
1033 | . $user->getId() . ' => ' . $user->getLogin() . '(' . get_class($user) . ')' . PHP_EOL
1034 | . 'and ' . $user->getComments()->count() . ' Entity\Comment attached to it: ' . PHP_EOL;
1035 | if($comment = $user->getComments()->first()) {
1036 | echo 'Removing the first attached comment!' . PHP_EOL;
1037 | echo 'Removing comment with id=' . $comment->getId() . PHP_EOL;
1038 | $em->remove($comment);
1039 | $em->flush();
1040 | } else {
1041 | echo 'Could not find any comments to remove...' . PHP_EOL;
1042 | }
1043 | } else {
1044 | echo 'Could not find Entity\User with id=1';
1045 | }
1046 |
1047 |
1048 |
1049 |
Joins in DQL: finding all users with a comment with id > 5
1050 |
1051 | //examples/11.php
1052 | require_once __DIR__ . '/../bootstrap.php';
1053 |
1054 | echo 'Searching all users with a comment with id > 5: ' . PHP_EOL;
1055 | $users = $em
1056 | ->createQuery('SELECT u FROM Entity\User u JOIN u.comments c WHERE c.id > :id')
1057 | ->setParameter('id', 5)
1058 | ->getResult();
1059 | echo 'Found ' . count($users) . ':' . PHP_EOL;
1060 | foreach($users as $user) {
1061 | echo ' ' . $user->getId() . ' => ' . $user->getLogin() . ' (' . get_class($user) . ')' . PHP_EOL;
1062 | }
1063 |
1064 |
1065 |
1066 |
Other cool features of Doctrine 2
1067 |
1068 | This tutorial should be extended with following examples and features:
1069 |
1070 |
1071 |
1072 | inheritance types
1073 |
1074 |
1075 | mapping types - custom mapping types
1076 |
1077 |
1078 | transactions
1079 |
1080 |
1081 | caches, performance, memory leaks
1082 |
1083 |
1084 | other mapping drivers
1085 |
1086 |
1087 | proxies
1088 |
1089 |
1090 | events
1091 |
1092 |
1093 | cascade operations
1094 |
1095 |
1096 | optimistic locking - pessimistic locking
1097 |
1098 |
1099 | NativeSQL
1100 |
1101 |
1102 | Query Builder
1103 |
1104 |
1105 | Custom repositories
1106 |
1107 |
1108 |
1109 |
1110 |
Other cool features of Doctrine 2
1111 |
1112 | This tutorial should be extended with following examples and features:
1113 |
1114 |
1115 |
1116 | other annotations:
1117 |
1118 | @Table
1119 | @Entity
1120 | @GeneratedValue
1121 | @JoinTable
1122 | @JoinColumn
1123 | @Index
1124 | @UniqueIndex
1125 | @OrderBy
1126 | @Version
1127 |
1128 |
1129 |
1130 |
1131 |
1132 |
Warnings
1133 | __clone
1134 | __wakeup
1135 | dumping
1136 | entity state
1137 |
1138 |
1158 |
1168 |
1169 |
1170 |
--------------------------------------------------------------------------------
/assets/font/TheSansMonoCondensed-SemiLight-webfont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
--------------------------------------------------------------------------------