├── .gitignore ├── .gitmodules ├── Gruntfile.coffee ├── README.md ├── assets ├── font │ ├── TheSansMonoCondensed-SemiLight-webfont.eot │ ├── TheSansMonoCondensed-SemiLight-webfont.svg │ ├── TheSansMonoCondensed-SemiLight-webfont.ttf │ ├── TheSansMonoCondensed-SemiLight-webfont.woff │ ├── TheSansMonoCondensed-SemiLight-webfont.woff2 │ ├── specimen_files │ │ ├── easytabs.js │ │ ├── grid_12-825-55-15.css │ │ └── specimen_stylesheet.css │ └── stylesheet.css ├── img │ ├── amusing-kitten.gif │ ├── awesome.png │ ├── bullshit-stamp.png │ ├── cqrs-diagram.png │ ├── cqrs.png │ ├── crud.jpg │ ├── doctrine.svg │ ├── gh.svg │ ├── github-white.png │ ├── github.svg │ ├── logo.png │ ├── ocramius.gif │ ├── roave.svg │ ├── sandwich.png │ ├── shit.jpg │ ├── twitter.svg │ └── zf-logo.svg └── style.css ├── bootstrap.php ├── bower.json ├── css └── source │ └── theme.scss ├── doctrine-cli.php ├── examples ├── 1.php ├── 10.php ├── 11.php ├── 2.php ├── 3.php ├── 4.php ├── 5.php ├── 6.php ├── 7.php ├── 8.php └── 9.php ├── image ├── Doctrine-Logo.png ├── common_icon.png ├── database-status.png └── databaseMapperSketch.gif ├── index.html ├── js └── loadhtmlslides.js ├── library └── Entity │ ├── Comment.php │ ├── Greeting.php │ └── User.php ├── package.json ├── slides ├── doctrine │ └── beginer.html ├── introduction │ ├── i-am-marco.html │ ├── i-work-for.html │ └── intro.html ├── list.json └── outro │ └── thanks.html ├── style └── slippy-styled.css └── templates ├── _index.html └── _section.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bower_components 3 | css/theme.css 4 | css/theme.css.map -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /assets/font/TheSansMonoCondensed-SemiLight-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/font/TheSansMonoCondensed-SemiLight-webfont.eot -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /assets/font/TheSansMonoCondensed-SemiLight-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/font/TheSansMonoCondensed-SemiLight-webfont.ttf -------------------------------------------------------------------------------- /assets/font/TheSansMonoCondensed-SemiLight-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/font/TheSansMonoCondensed-SemiLight-webfont.woff -------------------------------------------------------------------------------- /assets/font/TheSansMonoCondensed-SemiLight-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/font/TheSansMonoCondensed-SemiLight-webfont.woff2 -------------------------------------------------------------------------------- /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); -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /assets/img/amusing-kitten.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/amusing-kitten.gif -------------------------------------------------------------------------------- /assets/img/awesome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/awesome.png -------------------------------------------------------------------------------- /assets/img/bullshit-stamp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/bullshit-stamp.png -------------------------------------------------------------------------------- /assets/img/cqrs-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/cqrs-diagram.png -------------------------------------------------------------------------------- /assets/img/cqrs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/cqrs.png -------------------------------------------------------------------------------- /assets/img/crud.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/crud.jpg -------------------------------------------------------------------------------- /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/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/github-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/github-white.png -------------------------------------------------------------------------------- /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/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/logo.png -------------------------------------------------------------------------------- /assets/img/ocramius.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/ocramius.gif -------------------------------------------------------------------------------- /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/img/sandwich.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/sandwich.png -------------------------------------------------------------------------------- /assets/img/shit.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/assets/img/shit.jpg -------------------------------------------------------------------------------- /assets/img/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /assets/img/zf-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | ); -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ShittyCQRS explained", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "reveal.js": "~2.6.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /doctrine-cli.php: -------------------------------------------------------------------------------- 1 | new EntityManagerHelper($em), 10 | 'conn' => new ConnectionHelper($em->getConnection()) 11 | )); 12 | ConsoleRunner::run($helperSet); -------------------------------------------------------------------------------- /examples/1.php: -------------------------------------------------------------------------------- 1 | persist($greeting); 13 | 14 | //Flushing all changes to database 15 | $em->flush(); 16 | 17 | echo 'OK!'; -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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/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/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/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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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!'; -------------------------------------------------------------------------------- /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/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 | } -------------------------------------------------------------------------------- /image/Doctrine-Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/image/Doctrine-Logo.png -------------------------------------------------------------------------------- /image/common_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/image/common_icon.png -------------------------------------------------------------------------------- /image/database-status.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/image/database-status.png -------------------------------------------------------------------------------- /image/databaseMapperSketch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ocramius/Doctrine2ORMSlidesTutorial/9f73203b4826fc0ea59f33d5a2fad65d31b76856/image/databaseMapperSketch.gif -------------------------------------------------------------------------------- /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 | Doctrine 71 |

72 |
73 |
74 |

Some history of RDBMS persistence in PHP:

75 |
    76 |
  1. mysql_query, mysql_fetch_array, mysql_*
  2. 77 |
  3. mysqli, PDO
  4. 78 |
  5. Doctrine 1.2, Propel 1.6, Zend_Db
  6. 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 | 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 | 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 | 307 |
308 |
309 |

Doctrine 2

310 |
311 |
312 |

What is Doctrine 2?

313 | 338 |
339 |
340 |

Doctrine 2 Requirements

341 | 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 | Generated Database 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 | 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 | 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 |
852 |

853 | Some rules apply to relations, you can read them at 854 |
855 | 856 | http://www.doctrine-project.org/docs/orm/2.0/en/reference/association-mapping.html 857 | 858 |

859 |

860 | Please be sure to have read them before getting to work with Doctrine. 861 |

862 |
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 | 1108 |
1109 |
1110 |

Other cool features of Doctrine 2

1111 |

1112 | This tutorial should be extended with following examples and features: 1113 |

1114 | 1130 |
1131 |
1132 |

Warnings

1133 | __clone 1134 | __wakeup 1135 | dumping 1136 | entity state 1137 |
1138 |
1139 |

Please help the community!

1140 |

1141 | You can find, fork, edit and help me with these slides at 1142 |
1143 | 1144 | https://github.com/Ocramius/Doctrine2ORMSlidesTutorial 1145 | 1146 |

1147 |

1148 | You can find the Doctrine project pages at 1149 |
1150 | 1151 | http://www.doctrine-project.org/ 1152 | 1153 |

1154 |

1155 | All what you have seen is possible only because of a good community helping in this project, so please be part of it and share! 1156 |

1157 |
1158 |
1159 |

Thank You

1160 |

1161 | Marco Pivetta 1162 |
1163 | @Ocramius 1164 |
1165 | ocramius@gmail.com 1166 |

1167 |
1168 | 1169 | 1170 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /slides/doctrine/beginer.html: -------------------------------------------------------------------------------- 1 |

Doctrine best pratices

2 | 3 | -------------------------------------------------------------------------------- /slides/introduction/i-am-marco.html: -------------------------------------------------------------------------------- 1 |

I'm Marco!

2 | 3 |

-------------------------------------------------------------------------------- /slides/introduction/i-work-for.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /slides/introduction/intro.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /slides/outro/thanks.html: -------------------------------------------------------------------------------- 1 |

Thanks!

2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------