SlideShare a Scribd company logo
Get into the FLOW
                                 with Extbase




http://www.sxc.hu/photo/768249
Who is this?
Dipl.-Ing.
Mechanical
Engineering
infected with
TYPO3 in 2OO1
Tübingen
Get into the FLOW with Extbase
6O% selfemployed
   6O% father
8O%
---- selfemployed
6O%
   6O% father
5 years:
  Fraunhofer-
  Gesellschaft
German Aerospace
      Center
5 years:
high school
  teacher
What is Extbase
  all about?
1997
v4



 2OO9
v4
v4



2OO6
v5

v4
v5

v4
v5

v4
Extbase   v5

     v4
v5

v4
What is Extbase
  all about?
Extbase
FLOW 3
What is Extbase
  all about?
Extbase
OOP     A ggregate    DDD

       Extbase
                            V iew
          Entity           H elp er
M VC
                     VfB
  R epository
Extbase
 Value
Value

 enables and encourages the developer to write maintainable code
    separates di erent responsibilities
    modular architecture of the extension
 relieves the developer in safety-critical and recurring tasks
    validation of arguments
    invoking the template mechanism
    persistence
    read out the settings from TypoScript and FlexForms
 enables and encourages the developer to focus on solving the problem of the client
 saves primary and adapting development time (direct and indirect costs)
tp://commons.wikimedia.org/wiki/File:Z%C3%BCrich_-_Seefeld_-_Heureka_IMG_1757.JPG
tx_ttnews                                                         3397
                  tx_pbsurvey_pi1                                                   253O
                  tx_ttproducts_pi1                                                 1O9O
                  tx_mmforum_pi1                                                    6126
                  tx_veguestbook_pi1                                                1156

tp://commons.wikimedia.org/wiki/File:Z%C3%BCrich_-_Seefeld_-_Heureka_IMG_1757.JPG
!   /**
!    * Main news function: calls the init_news() function and decides by the given CODEs which of the
!    * functions to display news should by called.
!    *
!    * @param! string!    !    $content : function output is added to this
!    * @param! array!
                    !     $conf : configuration array
!    * @return!string!    !    $content: complete content generated by the tt_news plugin
!    */
!   function main_news($content, $conf) {
!   !    $this->local_cObj = t3lib_div::makeInstance('tslib_cObj'); // Local cObj.
!   !    $this->init($conf);

!   !    if ($this->conf['displayCurrentRecord']) {
!   !    !    $this->config['code'] = $this->conf['defaultCode']?trim($this->conf['defaultCode']):'SINGLE';
!   !    !    $this->tt_news_uid = $this->cObj->data['uid'];
!   !    }

!   !    // get codes and decide which function is used to process the content
!   !    $codes = t3lib_div::trimExplode(',', $this->config['code']?$this->config['code']:$this->conf['defaultCode'], 1);
!   !    if (!count($codes)) { // no code at all
!   !    !    $codes = array();
!   !    !    $noCode = true;
!   !    }

!   !    while (list(, $theCode) = each($codes)) {
!   !    !    $theCode = (string)strtoupper(trim($theCode));
!   !    !    $this->theCode = $theCode;
!   !    !    switch ($theCode) {
!   !    !    !     case 'SINGLE':
!   !    !    !     $content .= $this->displaySingle();
!   !    !    !     break;
!   !    !    !     case 'VERSION_PREVIEW':
!   !    !    !     $content .= $this->displayVersionPreview();
!   !    !    !     break;
!   !    !    !     case 'LATEST':
!   !    !    !     case 'LIST':
!   !    !    !     case 'SEARCH':
!   !    !    !     case 'XML':
!   !    !    !     $content .= $this->displayList();
!   !    !    !     break;
!   !    !    !     case 'AMENU':
!   !    !    !     $content .= $this->newsArchiveMenu();
!   !    !    !     break;
!   !    !    !     case 'CATMENU':
!   !    !    !     $content .= $this->displayCatMenu();
!   !    !    !     break;
!   !    !    !     default:
!   /**
!     * Displays the "single view" of a news article. Is also used when displaying single news records with the "insert records" content element.
!     *
!     * @return!string!    !    html-code for the "single view"
!     */
!   function displaySingle() {
!   !     $singleWhere = 'tt_news.uid=' . intval($this->tt_news_uid);
!   !     $singleWhere .= ' AND type NOT IN(1,2)' . $this->enableFields; // only real news -> type=0

!   !    $res   = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
!   !    !      '*',
!   !    !      'tt_news',
!   !    !      $singleWhere);

!   !    $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
!   !    // get the translated record if the content language is not the default language
!   !    if ($GLOBALS['TSFE']->sys_language_content) {
!   !    !    $OLmode = ($this->sys_language_mode == 'strict'?'hideNonTranslated':'');
!   !    !    $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay('tt_news', $row, $GLOBALS['TSFE']->sys_language_content, $OLmode);
!   !    }
!   !    if ($this->versioningEnabled) {
!   !    !    // get workspaces Overlay
!   !    !    $GLOBALS['TSFE']->sys_page->versionOL('tt_news',$row);
!   !    !    // fix pid for record from workspace
!   !    !    $GLOBALS['TSFE']->sys_page->fixVersioningPid('tt_news',$row);
!   !    }
!   !    $GLOBALS['TSFE']->displayedNews[]=$row['uid'];

!   !    if (is_array($row) && ($row['pid'] > 0 || $this->vPrev)) { // never display versions of a news record (having pid=-1) for normal website users
!   !    !    !     // Get the subpart code
!   !    !    if ($this->conf['displayCurrentRecord']) {
!   !    !    !     $item = trim($this->getNewsSubpart($this->templateCode, $this->spMarker('###TEMPLATE_SINGLE_RECORDINSERT###'), $row));
!   !    !    }
!   !    !    if (!$item) {
!   !    !    !     $item = $this->getNewsSubpart($this->templateCode, $this->spMarker('###TEMPLATE_SINGLE###'), $row);
!   !    !    }
!   !    !    !     // reset marker array
!   !    !    $wrappedSubpartArray = array();
!   !    !    !     // build the backToList link
!   !    !    if ($this->conf['useHRDates']) {
!   !    !    !     $pointerName = 'pointer';
!   !    !    !     $wrappedSubpartArray['###LINK_ITEM###'] = explode('|', $this->pi_linkTP_keepPIvars('|', array(
!   !    !    !     !    'tt_news' => null,
!   !    !    !     !    'backPid' => null,
!   !    !    !     !    $this->config['singleViewPointerName'] => null,
!   !    !    !     !    'pS' => null,
!   !    !    !     !    'pL' => null), $this->allowCaching, ($this->conf['dontUseBackPid']?1:0), $this->config['backPid']));
'pointer';
!                      !
!                      !
$wrappedSubpartArray['#
##LINK_ITEM###'] =
explode('|', $this-
>pi_linkTP_keepPIvars('
|', array(
!                      !
!                      !
!
'tt_news' => null,
!                      !
!                      !
!
'backPid' => null,
!                      !
!                      !
!
$this-
>config['singleViewPoin
terName'] => null,
!                      !
!                      !
!
'pS' => null,
!                      !
!                      !
!
'pL' => null), $this-
>allowCaching, ($this-
>conf['dontUseBackPid']
?1:0), $this-
>config['backPid']));
!                      !
!
} else {
!                      !
!                      !
$wrappedSubpartArray['#
##LINK_ITEM###'] =
explode('|', $this-
>pi_linkTP_keepPIvars('
|', array(
!                      !
!                      !
!
'tt_news' => null,
!                      !
!                      !
!
'backPid' => null,
!                      !
!                      !
!
$this-
>config['singleViewPoin
terName'] => null),
$this->allowCaching,
($this-
>conf['dontUseBackPid']
?1:0), $this-
>config['backPid']));
!                      !
!
}
!                      !
!                      !
// set the title of the
single view page to the
title of the news
record
!                      !
!
if ($this-
>conf['substitutePageti
tle']) {
!                      !
!                      !
$GLOBALS['TSFE']-
>page['title'] =
$row['title'];
!                      !
!                      !
// set pagetitle for
indexed search to news
title
!                      !
!                      !
$GLOBALS['TSFE']-
>indexedDocTitle =
$row['title'];
!                      !
!
}
!                      !
!
if ($this-
>conf['displaySingle.']
['catOrderBy']) {
!                      !
!                      !
$this-
>config['catOrderBy'] =
$this-
>conf['displaySingle.']
['catOrderBy'];
!                      !
!
}
!                      !
!
$markerArray = $this-
>getItemMarkerArray($ro
w, 'displaySingle');
!                      !
!
// Substitute
!                      !
!
$content = $this->cObj-
>substituteMarkerArrayC
ached($item,
$markerArray, array(),
$wrappedSubpartArray);
!                      !
} elseif ($this-
>sys_language_mode ==
'strict' && $this-
>tt_news_uid &&
$GLOBALS['TSFE']-
>sys_language_content)
{ // not existing
translation
!                      !
!
$noTranslMsg = $this-
>local_cObj-
>stdWrap($this-
>pi_getLL('noTranslMsg'
), $this-
>conf['noNewsIdMsg_stdW
rap.']);
!                      !
!
$content =
$noTranslMsg;
!                      !
} elseif ($row['pid'] <
0) { // a non-public
version of a record was
requested
!                      !
!
$nonPlublicVersion =
$this->local_cObj-
>stdWrap($this-
>pi_getLL('nonPlublicVe
rsionMsg'), $this-
>conf['nonPlublicVersio
nMsg_stdWrap.']);
!                      !
!
$content =
$nonPlublicVersion;
!                      !
} else { // if
singleview is shown
with no tt_news uid
given from GETvars
(&tx_ttnews[tt_news]=)
an error message is
displayed.
!                      !
!
$noNewsIdMsg = $this-
>local_cObj-
>stdWrap($this-
>pi_getLL('noNewsIdMsg'
), $this-
Get into the FLOW with Extbase
control flow

business logic

CRUD data

render output

other , eg. config
control flow

            business logic

aggregate   CRUD data

            render output

            other , eg. config
control flow

             business logic

generalize   CRUD data

             render output

             other , eg. config
partition
Extbase
use
      & Fluid
Get into the FLOW with Extbase
control flow

business logic

CRUD data

render output

other , eg. config
Controller

Domain Model

Domain Repository

View

other , eg. config
Blog features

 administrate blogs, blog posts and blog comments
 list all available blogs
 list all blog posts of a blog
 list all comments of a blog post
 allow users to post new comments
Blog features

 administrate blogs, blog posts and blog comments
                                                    Blog
 list all available blogs
 list all blog posts of a blog
 list all comments of a blog post
 allow users to post new comments
Blog features

 administrate blogs, blog posts and blog comments
                                                              Blog
 list all available blogs
 list all blog posts of a blog
 list all comments of a blog post
 allow users to post new comments                             Post




                                                    Comment          Tag
TYPO3
tslib_piBase
                                  tx_blog
           1
        userFunc


TYPO3              tx_blog_pi
tslib_piBase
                                  tx_blog
           1
        userFunc                   2
                                  exec_SELECTgetRows
TYPO3              tx_blog_pi
                                                       Database
tslib_piBase
                                     tx_blog
           1
        userFunc                        2
                                    exec_SELECTgetRows
TYPO3              tx_blog_pi

                                  rows as                Database
                                   array

                                    3
tslib_piBase
                                     tx_blog
           1
        userFunc                        2
                                    exec_SELECTgetRows
TYPO3              tx_blog_pi
         HTML
                                  rows as                Database
           4                       array

                                    3
Control ow

public function main($content, $conf) {
! $this->conf = $conf;
! $this->pi_setPiVarDefaults();
! $this->pi_loadLL();

!   if ($this->piVars['postUid']) {
!   ! if ($this->piVars['newComment']) {
!   ! ! $this->storeNewComment();
!   ! }
!   ! $content = $this->renderPost();
!   } elseif ($this->piVars['blogUid']) {
!   ! $content = $this->renderBlog();
!   } else {
!   ! $content = $this->renderListOfBlogs();
!   }
!   return $this->pi_wrapInBaseClass($content);
}
Task 1: Output a listing of blogs

protected function renderListOfBlogs() {
! $blogs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! '*',
! ! 'tx_blogexample_blog',
! ! 'sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid
         . $this->cObj->enableFields('tx_blogexample_blog'),
! ! '',
! ! 'date'
! ! );



!   $template = $this->cObj->fileResource($this->conf['template']);
!   $blogElementSubpart = $this->cObj->getSubpart($template, '###SUBPART_BLOGELEMENT###');
Task 1: Output a listing of blogs


!   foreach ($blogs as $blog) {
!   ! $linkParameters = array('blogUid' => $blog['uid']);
!   ! $markers = array(
!   ! ! '###BLOG_NAME###' => $blog['name'],
!   ! ! '###BLOG_LOGO###' => $this->cImage('uploads/tx_blog/' . $blog['logo']),
!   ! ! '###BLOG_DESCRIPTION###' => $this->pi_RTEcssText($blog['description']),
!   ! ! '###BLOG_MORELINK###' => $this->pi_linkTP('show blog', $linkParameters, true),
!   ! );
!   ! $blogElements.= $this->cObj->substituteMarkerArray($blogElementSubpart, $markers);
!   }
!   return $content;
}
Task 1: Output a listing of blogs


<!-- ###SUBPART_BLOGELEMENT### begin -->
<div class="blog element">
! ###BLOG_NAME###
! ###BLOG_LOGO###
! ###BLOG_DESCRIPTION###
! ###BLOG_MORELINK###
</div>
<!-- ###SUBPART_BLOGELEMENT### end -->
Task 2: Display a single post with its comments

protected function renderPost() {
! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']);
! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! '*',
! ! 'tx_blogexample_comment',
! ! 'deleted=0 AND hidden=0 AND sys_language_uid='
          . $GLOBALS['TSFE']->sys_language_uid
! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"'
          . $this->cObj->enableFields('tx_blogexample_comment'),
! ! '',
! ! 'date DESC'
! );

// fill marker arrays and substitute in template
// return content
}
Task 3: Add a new comment to a blog post

 the whole plugin is cached („USER“)
 dynamic user input won‘t outputted instantly
 de ne uncached behavior in TypoScript


             [globalVar = _POST:tx_blogexample_pi1|newComment = 1]
             ! plugin.tx_blogexample_pi1 = USER_INT
             [globals]
Task 3: Add a new comment to a blog post

protected function storeNewComment() {
! $fields = array(
! ! 'post_uid' => $this->piVars['postUid'],
! ! 'post_table' => 'tx_blogexample_post',
! ! 'date' => time(),
! ! 'author' => $this->piVars['author'],
! ! 'email' => $this->piVars['email'],
! ! 'content' => $this->piVars['content'],
! );

!   $GLOBALS['TYPO3_DB']->exec_INSERTquery(
!   ! 'tx_blogexample_comment', $fields
!   );
}
Security: SQL injections
protected function renderPost() {
! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']);
! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! '*',
! ! 'tx_blogexample_comment',
! ! 'deleted=0 AND hidden=0 AND sys_language_uid='
          . $GLOBALS['TSFE']->sys_language_uid
! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"'
          . $this->cObj->enableFields('tx_blogexample_comment'),
! ! '',
! ! 'date DESC'
! );
[...]
Security: SQL injections
protected function renderPost() {
! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']);
! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! '*',
! ! 'tx_blogexample_comment',
! ! 'deleted=0 AND hidden=0 AND sys_language_uid='
          . $GLOBALS['TSFE']->sys_language_uid
! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"'
          . $this->cObj->enableFields('tx_blogexample_comment'),
! ! '',
! ! 'date DESC'
! );
[...]

  http://www.example.com/index.php&id=99&postUid=1
Security: SQL injections
protected function renderPost() {
! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']);
! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! '*',
! ! 'tx_blogexample_comment',
! ! 'deleted=0 AND hidden=0 AND sys_language_uid='
          . $GLOBALS['TSFE']->sys_language_uid
! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"'
          . $this->cObj->enableFields('tx_blogexample_comment'),
! ! '',
! ! 'date DESC'
! );
[...]

  http://www.example.com/index.php&id=99&postUid=1



SELECT * FROM tx_blog_comment WHERE post_uid=1;
Security: SQL injections
protected function renderPost() {
! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']);
! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! '*',
! ! 'tx_blogexample_comment',
! ! 'deleted=0 AND hidden=0 AND sys_language_uid='
          . $GLOBALS['TSFE']->sys_language_uid
! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"'
          . $this->cObj->enableFields('tx_blogexample_comment'),
! ! '',
! ! 'date DESC'
! );
[...]

  http://www.example.com/index.php&id=99&postUid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1



SELECT * FROM tx_blog_comment WHERE post_uid=1;
INSERT INTO be_users SET ...;
SELECT * FROM tx_blog_comment WHERE 1=1 AND post_table="tx_blogexample_post"
Security: SQL injections
protected function renderPost() {
! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']);
! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! '*',
! ! 'tx_blogexample_comment',
! ! 'deleted=0 AND hidden=0 AND sys_language_uid='
          . $GLOBALS['TSFE']->sys_language_uid
! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"'
          . $this->cObj->enableFields('tx_blogexample_comment'),
! ! '',
! ! 'date DESC'
! );
[...]

  http://www.example.com/index.php&id=99&postUid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1



SELECT * FROM tx_blog_comment WHERE post_uid=1;
INSERT INTO be_users SET ...;
SELECT * FROM tx_blog_comment WHERE 1=1 AND post_table="tx_blogexample_post"
Security: SQL injections
protected function renderPost() {
! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']);
! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! '*',
! ! 'tx_blogexample_comment',
! ! 'deleted=0 AND hidden=0 AND sys_language_uid=
          . $GLOBALS['TSFE']->sys_language_uid
! ! ! . ' AND post_uid=' . intval($this->piVars['postUid']) . ' AND post_table="tx_blogexample_post"'
          . $this->cObj->enableFields('tx_blogexample_comment'),
! ! '',
! ! 'date DESC'
! );
[...]

  http://www.example.com/index.php&id=99&postUid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1



SELECT * FROM tx_blog_comment WHERE post_uid=1;
INSERT INTO be_users SET ...;
SELECT * FROM tx_blog_comment WHERE 1=1 AND post_table="tx_blogexample_post"
http://www.sxc.hu/photo/516864/




                                  Extension building
                                        with Extbase
TYPO3
1
        userFunc

                    Extbase
TYPO3              Dispatcher
1
                                  2
        userFunc
                                Request
                                               BlogExample
                    Extbase
TYPO3              Dispatcher
                                          Controller
1
                                       2
        userFunc
                                    Request
                                                     BlogExample
                    Extbase
TYPO3              Dispatcher
                                              Controller

                            3
                         findByTitle('MyBlog')




                            Repository


                                                 Domain Model
                                    Blog


                                    Post



                           Comment         Tag
1
                                       2
        userFunc
                                    Request
                                                        BlogExample
                    Extbase
TYPO3              Dispatcher
                                              Controller

                            3
                         findByTitle('MyBlog')
                                                 Blog

                                                   4
                            Repository


                                                 Domain Model
                                    Blog


                                    Post



                           Comment         Tag
1
                                       2
        userFunc
                                    Request
                                                        BlogExample
                    Extbase
TYPO3              Dispatcher
                                              Controller

                            3                                   assign(Blog)

                         findByTitle('MyBlog')                   render()      5
                                                 Blog

                                                   4                   View
                            Repository


                                                 Domain Model
                                    Blog


                                    Post



                           Comment         Tag
1
                                       2
        userFunc
                                    Request
                                                         BlogExample
                    Extbase
TYPO3              Dispatcher
         HTML
                                 Response     Controller
          6
                            3                                    assign(Blog)

                         findByTitle('MyBlog')                       render()     5
                                                  Blog
                                                          Response
                                                    4                      View
                            Repository


                                                 Domain Model
                                    Blog


                                    Post



                           Comment          Tag
1
                                       2
        userFunc
                                    Request
                                                         BlogExample
                    Extbase
TYPO3              Dispatcher
         HTML
                                 Response     Controller
          6
                            3                                    assign(Blog)

                         findByTitle('MyBlog')                       render()     5
                                                  Blog
                                                          Response
                                                    4                      View
                            Repository


                                                 Domain Model
                                    Blog


                                    Post



                           Comment          Tag
Blog




          Post




Comment          Tag
Model
/**
 * A blog
 *
 * @version $Id:$
 * @copyright Copyright belongs to the respective authors
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License, version 2
 * @scope prototype
 * @entity
 */
class Tx_BlogExample_Domain_Model_Blog extends Tx_Extbase_DomainObject_AbstractEntity {

!   /**
!    * The blog's title.
!    *
!    * @var string
!    * @validate Text, StringLength(minimum = 1, maximum = 80)
!    * @identity
!    */
!   protected $title = '';

!   /**
!    * A short description of the blog
!    *
!    * @var string
!    * @validate Text, StringLength(maximum = 150)
!    */
!   protected $description = '';
!   }

!   /**
!    * Sets this blog's title
!    *
!    * @param string $title The blog's title
!    * @return void
!    */
!   public function setTitle($title) {
!   ! $this->title = $title;
!   }

!   /**
!     * Returns the blog's title
!     *
!     * @return string The blog's title
!     */
!   public function getTitle() {
!   ! return $this->title;
!   }

!   /**
!    * Sets the description for the blog
!    *
!    * @param string $description
!    * @return void
!    */
!   public function setDescription($description) {
!   ! $this->description = $description;
!     * @return Tx_Extbase_Persistence_ObjectStorage
!     */
!   public function getPosts() {
!   ! return $this->posts;
!   }

!   /**
!     * Sets the administrator value
!     *
!     * @param Tx_BlogExample_Domain_Model_Administrator $administrator The Administrator of this Blog
!     * @return void
!     */
!   public function setAdministrator(Tx_BlogExample_Domain_Model_Administrator $administrator) {
!   ! $this->administrator = $administrator;
!   }

!   /**
!     * Returns the administrator value
!     *
!     * @return Tx_BlogExample_Domain_Model_Administrator
!     */
!   public function getAdministrator() {
!   ! return $this->administrator;
!   }

}
/**
 * A blog post comment
 *
 * @package Blog
 * @subpackage Domain
 * @version $Id:$
 * @copyright Copyright belongs to the respective authors
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License, version 2
 * @scope prototype
 * @entity
 */
class Tx_BlogExample_Domain_Model_Comment extends Tx_Extbase_DomainObject_AbstractEntity {

!   /**
!    * @var DateTime
!    */
!   protected $date;

!   /**
!    * @var string
!    * @validate Text, StringLength(minimum = 3, maximum = 80)
!    */
!   protected $author;

!   /**
!    * @var string
!    * @validate EmailAddress
!    * @param string $content
!    * @return void
!    */
!   public function setContent($content) {
!   ! $this->content = $content;
!   }

!   /**
!     * Getter for content
!     *
!     * @return string
!     */
!   public function getContent() {
!   ! return $this->content;
!   }

!   /**
!    * Returns this comment as a formatted string
!    *
!    * @return string
!    */
!   public function __toString() {
!   ! return $this->author . ' (' . $this->email . ') said on ' . $this->date->format('Y-m-d') . ':' . chr(10)
!   ! ! $this->content . chr(10);
!   }
}
http://www.sxc.hu/photo/444174/




                                  and...




                                  action
/**
 * The blog controller for the Blog package
 *
 * @version $Id:$
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License, version 2
 */
class Tx_BlogExample_Controller_BlogController extends Tx_Extbase_MVC_Controller_ActionController {

!   /**
!    * @var Tx_BlogExample_Domain_Model_BlogRepository
!    */
!   protected $blogRepository;

!   /**
!     * Initializes the current action
!     *
!     * @return void
!     */
!   public function initializeAction() {
!   !    $this->blogRepository = t3lib_div::makeInstance('Tx_BlogExample_Domain_Repository_BlogRepository');
!   !    $this->postRepository = t3lib_div::makeInstance('Tx_BlogExample_Domain_Repository_PostRepository');
!   !    $this->administratorRepository = t3lib_div::makeInstance('Tx_BlogExample_Domain_Repository_AdministratorRepository');
!   }

!   /**
!     * Index action for this controller. Displays a list of blogs.
!     *
!     * @return string The rendered view
!     */
!   public function indexAction() {
!   !    $this->view->assign('blogs', $this->blogRepository->findAll());
!   }
!   }

!   /**
!     * Index action for this controller. Displays a list of blogs.
!     *
!     * @return string The rendered view
!     */
!   public function indexAction() {
!   !    $this->view->assign('blogs', $this->blogRepository->findAll());
!   }

!   /**
!    * Displays a form for creating a new blog
!    *
!    * @param Tx_BlogExample_Domain_Model_Blog $newBlog A fresh blog object taken as a basis for the rendering
!    * @return string An HTML form for creating a new blog
!    * @dontvalidate $newBlog
!    */
!   public function newAction(Tx_BlogExample_Domain_Model_Blog $newBlog = NULL) {
!   !   $this->view->assign('newBlog', $newBlog);
!   !   $this->view->assign('administrators', $this->administratorRepository->findAll());
!   }

!   /**
!    * Creates a new blog
!    *
!    * @param Tx_BlogExample_Domain_Model_Blog $newBlog A fresh Blog object which has not yet been added to the repository
!    * @return void
!    */
!   public function createAction(Tx_BlogExample_Domain_Model_Blog $newBlog) {
!   !   $this->blogRepository->add($newBlog);
!   !   $this->pushFlashMessage('Your new blog was created.');
!   !   $this->redirect('index');
!   }
How could you
fetch a blog?
How could you
fetch a book?
Model

                  Blog




                  Post




        Comment          Tag
Model

        BlogRepository
                                   Blog




                                   Post




                         Comment          Tag
/**
 * A repository for Blogs
 */
class Tx_BlogExample_Domain_Repository_BlogRepository extends Tx_Extbase_Persistence_Repository {
!
! /**
!   * Remove the blog's posts before removing the blog itself.
!   *
!   * @return array An array filled with blogs
!   */
! public function findAll() {
! ! $blogs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
! ! ! '*',
! ! ! 'tx_blogexample_domain_model_blog',
! ! ! 'sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid
           . $this->cObj->enableFields('tx_blogexample_domain_model_blog'),
! ! ! '',
! ! ! 'date'
! ! ! );
! ! !
! ! return $blogs;
! }
}
/**
 * A repository for Blogs
 */
class Tx_BlogExample_Domain_Repository_BlogRepository extends Tx_Extbase_Persistence_Repository {
!
}
/**
 * A repository for Posts
 */
class Tx_BlogExample_Domain_Repository_PostRepository extends Tx_Extbase_Persistence_Repository {
!
! /**
!   * Finds posts by the specified blog
!   *
!   * @param Tx_BlogExample_Domain_Model_Blog $blog The blog the post must refer to
!   * @param integer $limit The number of posts to return at max
!   * @return array The posts
!   */
! public function findByBlog(Tx_BlogExample_Domain_Model_Blog $blog, $limit = 20) {
! ! $query = $this->createQuery();
! ! return $query->matching($query->equals('blog', $blog))
! ! ! ->setOrderings(array('date' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING))
! ! ! ->setLimit($limit)
! ! ! ->execute();
! }

!   /**
!    * Finds the previous of the given post
!    *
!    * @param Tx_BlogExample_Domain_Model_Post $post The reference post
!    * @return Tx_BlogExample_Domain_Model_Post
!    */
!   public function findPrevious(Tx_BlogExample_Domain_Model_Post $post) {
!    * @param Tx_BlogExample_Domain_Model_Post $post The reference post
!    * @return Tx_BlogExample_Domain_Model_Post
!    */
!   public function findNext(Tx_BlogExample_Domain_Model_Post $post) {
!   ! $query = $this->createQuery();
!   ! $posts = $query->matching($query->greaterThan('date', $post->getDate()))
!   ! ! ->setOrderings(array('date' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING))
!   ! ! ->setLimit(1)
!   ! ! ->execute();
!   ! return (count($posts) == 0) ? NULL : current($posts);
!   }

!   /**
!    * Finds most recent posts by the specified blog
!    *
!    * @param Tx_BlogExample_Domain_Model_Blog $blog The blog the post must refer to
!    * @param integer $limit The number of posts to return at max
!    * @return array The posts
!    */
!   public function findRecentByBlog(Tx_BlogExample_Domain_Model_Blog $blog, $limit = 5) {
!   ! $query = $this->createQuery();
!   ! return $query->matching($query->equals('blog', $blog))
!   ! ! ->setOrderings(array('date' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING))
!   ! ! ->setLimit($limit)
!   ! ! ->execute();
!   }
}
Repositories

 Encapsulate all data access
 SQL is allowed only in the Repository
 ... but not necessary anymore: use the Query object instead
 Magic methods: ndBy*, ndOneBy*
Blog




          Post




Comment          Tag
Aggregate

                Blog




                Post




      Comment          Tag
Blog


Entity

                   Post




         Comment          Tag
Blog


Entity

                   Post


                                Value
                                Object
         Comment          Tag
Aggregate
                                  Root
                   Blog


Entity

                   Post


                                  Value
                                  Object
         Comment          Tag
Aggregate
getLatestComment()                            Root
                               Blog




                               Post




                     Comment          Tag
Aggregate
getLatestComment()                            Root
                               Blog




                               Post




                     Comment          Tag
Aggregate
getLatestComment()                            Root
                               Blog




                               Post




                     Comment          Tag
<h1 class="csc-firstHeader">Welcome to {blog.title}</h1>
<p class="bodytext">{blog.description}</p>
<div class="tx-blogexample-list-container">
!    <f:if condition="{blog.posts}">
!    !    <f:then>
!    !    <p class="bodytext"><f:translate key="label_recent_posts">Below are the most recent posts:</f:translate></p>
!    !    !    <ul>
!    !    !    !    <f:for each="{blog.posts}" as="post">
!    !    !    !    !    <li>
!    !    !    !    !    !    <h3>
!    !    !    !    !    !    !    <f:format.date>{post.date}</f:format.date>
!    !    !    !    !    !    !    <f:link.action action="show" controller="Post" arguments="{post: post, blog: post.blog}">{post.title}</f:link.action>
!    !    !    !    !    !    </h3>
!    !    !    !    !    !    <p class="bodytext"><f:format.crop maxCharacters="100">{post.content}</f:format.crop></p>
!    !    !    !    !    !    <p>
!    !    !    !    !    !    !    By: {post.author.fullName}<br />
!    !    !    !    !    !    !    Tags: <f:for each="{post.tags}" as="tag">[{tag.name}]&nbsp;</f:for><br />
!    !    !    !    !    !    !    <f:link.action controller="Post" action="show" arguments="{post : post}">
                                        <f:translate key="read_more">read more &gt;&gt;</f:translate>
                                   </f:link.action><br />
!    !    !    !    !    !    !    <f:link.action controller="Post" action="edit" arguments="{post : post, blog : blog}">
                                        Edit
                                   </f:link.action>&nbsp;
                                   <f:link.action controller="Post" action="delete" arguments="{post : post, blog : blog}">
                                        Delete
                                   </f:link.action>
!    !    !    !    !    !    </p>
!    !    !    !    !    </li>
!    !    !    !    </f:for>
!    !    !    </ul>
!    !    </f:then>
!    !    <f:else>
!    !    !    <p>This blog currently doesn't contain any posts.</p>
!    !    </f:else>
!    </f:if>
!    <p><f:link.action action="new" controller="Post" arguments="{blog : blog}">Create a new Post</f:link.action></p>
</div>
Security

 All arguments must be registered.
 Registration of expected arguments happens through de ning them as method
 parameters.
 PHPDoc is mandatory as it is used for data type validation
*/
class Tx_BlogExample_Domain_Model_Comment extends Tx_Extbase_DomainObject_AbstractEntity {

!   /**
!    * @var DateTime
!    */
!   protected $date;

!   /**
!    * @var string
!    * @validate Text, StringLength(minimum = 3, maximum = 80)
!    */
!   protected $author;

!   /**
!    * @var string
!    * @validate EmailAddress
!    */
!   protected $email;

!   /**
!    * @var string
!    * @validate Text, NotEmpty
!    */
!   protected $content;
!
!
!   /**
!    * Constructs this post
Controlle
                r




                  odel
      Do main / M




             View




Con
   fig
       ura
           tio
               n
Principles of Domain Driven Design

 focus on the domain = activity or business of user
   we start with the business logic (PHP classes)
   we don't care about the database backend / persistence layer
 objects represent things in the real world, with their attributes and behavior
 ubiquitous language
 building blocks
   Entity
   Value Objects
   Repositories
What's next?
What's next

 New Kickstarter
   currently ongoing project by the core development team
   will be released shortly after 4.3
   Domain-Driven Design - Don't think in databases, think in Domain Models!
 Support for BE-Modules
   experimental by now
 AJAX-Dispatcher
 Dependency Injection
 PDO Storage Backend
Get into the FLOW with Extbase
-


          Modern
    architecture
Modular
reusable
 Greatly reusable
components
   components
Easy and
clean API
Easy testable
Get more
implemented
in less time
You will
get addicted
Bastian Waidelich
          Sebastian Kurfürst




         Than k You                                Steffen Kamper




                  and the TYPO3 V5 Team for all the inspiration
                             and the beautiful code
Ingmar Schlecht
                                                     Christopher Hlubek


                              Xavier Perseguers
Resources and links

 Project web site: http://forge.typo3.org/projects/show/typo3v4-mvc
 SVN: https://svn.typo3.org/TYPO3v4/CoreProjects/MVC/
 Newslist typo3.projects.typo3v4mvc@lists.net elders.de
 First stable release with TYPO3 4.3 alpha3: http://typo3.org/download/packages/

More Related Content

PDF
[PL] Jak nie zostać "programistą" PHP?
PDF
Refactoring using Codeception
PDF
Symfony2 - WebExpo 2010
PDF
What's New in Perl? v5.10 - v5.16
PDF
WordPress Security: Be a Superhero - WordCamp Raleigh - May 2011
TXT
PDF
PHP Tips & Tricks
PDF
PhpBB meets Symfony2
[PL] Jak nie zostać "programistą" PHP?
Refactoring using Codeception
Symfony2 - WebExpo 2010
What's New in Perl? v5.10 - v5.16
WordPress Security: Be a Superhero - WordCamp Raleigh - May 2011
PHP Tips & Tricks
PhpBB meets Symfony2

What's hot (20)

PDF
Perl6 grammars
TXT
Bouncingballs sh
PDF
Symfony2 - OSIDays 2010
PDF
Perl 6 by example
PDF
I, For One, Welcome Our New Perl6 Overlords
PDF
The Magic Of Tie
PDF
The state of Symfony2 - SymfonyDay 2010
PDF
KEY
Hidden treasures of Ruby
ODP
PDF
PHP 8.1: Enums
PDF
8時間耐久CakePHP2 勉強会
PDF
Perl Bag of Tricks - Baltimore Perl mongers
PDF
Advanced modulinos trial
PPTX
London XQuery Meetup: Querying the World (Web Scraping)
PDF
ภาษา C
PDF
Dependency injection - phpday 2010
PDF
PHPUnit でよりよくテストを書くために
Perl6 grammars
Bouncingballs sh
Symfony2 - OSIDays 2010
Perl 6 by example
I, For One, Welcome Our New Perl6 Overlords
The Magic Of Tie
The state of Symfony2 - SymfonyDay 2010
Hidden treasures of Ruby
PHP 8.1: Enums
8時間耐久CakePHP2 勉強会
Perl Bag of Tricks - Baltimore Perl mongers
Advanced modulinos trial
London XQuery Meetup: Querying the World (Web Scraping)
ภาษา C
Dependency injection - phpday 2010
PHPUnit でよりよくテストを書くために
Ad

Viewers also liked (6)

PDF
2012 08-11-flow3-northeast-php
PDF
Semantic TYPO3
PDF
Extbase and Beyond
PDF
Future Challenges for TYPO3
PDF
The Web as Application Platform Driven by Semantic Technologies
PDF
TYPO3 5.0 - Der aktuelle Stand der Zukunft
2012 08-11-flow3-northeast-php
Semantic TYPO3
Extbase and Beyond
Future Challenges for TYPO3
The Web as Application Platform Driven by Semantic Technologies
TYPO3 5.0 - Der aktuelle Stand der Zukunft
Ad

Recently uploaded (20)

PDF
Encapsulation theory and applications.pdf
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
KodekX | Application Modernization Development
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Spectral efficient network and resource selection model in 5G networks
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PPTX
Big Data Technologies - Introduction.pptx
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
Review of recent advances in non-invasive hemoglobin estimation
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
Encapsulation theory and applications.pdf
Mobile App Security Testing_ A Comprehensive Guide.pdf
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
KodekX | Application Modernization Development
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Unlocking AI with Model Context Protocol (MCP)
MIND Revenue Release Quarter 2 2025 Press Release
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Diabetes mellitus diagnosis method based random forest with bat algorithm
Encapsulation_ Review paper, used for researhc scholars
Spectral efficient network and resource selection model in 5G networks
20250228 LYD VKU AI Blended-Learning.pptx
Big Data Technologies - Introduction.pptx
NewMind AI Weekly Chronicles - August'25 Week I
Programs and apps: productivity, graphics, security and other tools
Network Security Unit 5.pdf for BCA BBA.
Review of recent advances in non-invasive hemoglobin estimation
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Digital-Transformation-Roadmap-for-Companies.pptx

Get into the FLOW with Extbase

  • 1. Get into the FLOW with Extbase http://www.sxc.hu/photo/768249
  • 7. 6O% selfemployed 6O% father
  • 9. 5 years: Fraunhofer- Gesellschaft German Aerospace Center
  • 11. What is Extbase all about?
  • 12. 1997
  • 14. v4
  • 16. v5 v4
  • 17. v5 v4
  • 18. v5 v4
  • 19. Extbase v5 v4
  • 20. v5 v4
  • 21. What is Extbase all about?
  • 23. What is Extbase all about?
  • 25. OOP A ggregate DDD Extbase V iew Entity H elp er M VC VfB R epository
  • 27. Value enables and encourages the developer to write maintainable code separates di erent responsibilities modular architecture of the extension relieves the developer in safety-critical and recurring tasks validation of arguments invoking the template mechanism persistence read out the settings from TypoScript and FlexForms enables and encourages the developer to focus on solving the problem of the client saves primary and adapting development time (direct and indirect costs)
  • 29. tx_ttnews 3397 tx_pbsurvey_pi1 253O tx_ttproducts_pi1 1O9O tx_mmforum_pi1 6126 tx_veguestbook_pi1 1156 tp://commons.wikimedia.org/wiki/File:Z%C3%BCrich_-_Seefeld_-_Heureka_IMG_1757.JPG
  • 30. ! /** ! * Main news function: calls the init_news() function and decides by the given CODEs which of the ! * functions to display news should by called. ! * ! * @param! string! ! $content : function output is added to this ! * @param! array! ! $conf : configuration array ! * @return!string! ! $content: complete content generated by the tt_news plugin ! */ ! function main_news($content, $conf) { ! ! $this->local_cObj = t3lib_div::makeInstance('tslib_cObj'); // Local cObj. ! ! $this->init($conf); ! ! if ($this->conf['displayCurrentRecord']) { ! ! ! $this->config['code'] = $this->conf['defaultCode']?trim($this->conf['defaultCode']):'SINGLE'; ! ! ! $this->tt_news_uid = $this->cObj->data['uid']; ! ! } ! ! // get codes and decide which function is used to process the content ! ! $codes = t3lib_div::trimExplode(',', $this->config['code']?$this->config['code']:$this->conf['defaultCode'], 1); ! ! if (!count($codes)) { // no code at all ! ! ! $codes = array(); ! ! ! $noCode = true; ! ! } ! ! while (list(, $theCode) = each($codes)) { ! ! ! $theCode = (string)strtoupper(trim($theCode)); ! ! ! $this->theCode = $theCode; ! ! ! switch ($theCode) { ! ! ! ! case 'SINGLE': ! ! ! ! $content .= $this->displaySingle(); ! ! ! ! break; ! ! ! ! case 'VERSION_PREVIEW': ! ! ! ! $content .= $this->displayVersionPreview(); ! ! ! ! break; ! ! ! ! case 'LATEST': ! ! ! ! case 'LIST': ! ! ! ! case 'SEARCH': ! ! ! ! case 'XML': ! ! ! ! $content .= $this->displayList(); ! ! ! ! break; ! ! ! ! case 'AMENU': ! ! ! ! $content .= $this->newsArchiveMenu(); ! ! ! ! break; ! ! ! ! case 'CATMENU': ! ! ! ! $content .= $this->displayCatMenu(); ! ! ! ! break; ! ! ! ! default:
  • 31. ! /** ! * Displays the "single view" of a news article. Is also used when displaying single news records with the "insert records" content element. ! * ! * @return!string! ! html-code for the "single view" ! */ ! function displaySingle() { ! ! $singleWhere = 'tt_news.uid=' . intval($this->tt_news_uid); ! ! $singleWhere .= ' AND type NOT IN(1,2)' . $this->enableFields; // only real news -> type=0 ! ! $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( ! ! ! '*', ! ! ! 'tt_news', ! ! ! $singleWhere); ! ! $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); ! ! // get the translated record if the content language is not the default language ! ! if ($GLOBALS['TSFE']->sys_language_content) { ! ! ! $OLmode = ($this->sys_language_mode == 'strict'?'hideNonTranslated':''); ! ! ! $row = $GLOBALS['TSFE']->sys_page->getRecordOverlay('tt_news', $row, $GLOBALS['TSFE']->sys_language_content, $OLmode); ! ! } ! ! if ($this->versioningEnabled) { ! ! ! // get workspaces Overlay ! ! ! $GLOBALS['TSFE']->sys_page->versionOL('tt_news',$row); ! ! ! // fix pid for record from workspace ! ! ! $GLOBALS['TSFE']->sys_page->fixVersioningPid('tt_news',$row); ! ! } ! ! $GLOBALS['TSFE']->displayedNews[]=$row['uid']; ! ! if (is_array($row) && ($row['pid'] > 0 || $this->vPrev)) { // never display versions of a news record (having pid=-1) for normal website users ! ! ! ! // Get the subpart code ! ! ! if ($this->conf['displayCurrentRecord']) { ! ! ! ! $item = trim($this->getNewsSubpart($this->templateCode, $this->spMarker('###TEMPLATE_SINGLE_RECORDINSERT###'), $row)); ! ! ! } ! ! ! if (!$item) { ! ! ! ! $item = $this->getNewsSubpart($this->templateCode, $this->spMarker('###TEMPLATE_SINGLE###'), $row); ! ! ! } ! ! ! ! // reset marker array ! ! ! $wrappedSubpartArray = array(); ! ! ! ! // build the backToList link ! ! ! if ($this->conf['useHRDates']) { ! ! ! ! $pointerName = 'pointer'; ! ! ! ! $wrappedSubpartArray['###LINK_ITEM###'] = explode('|', $this->pi_linkTP_keepPIvars('|', array( ! ! ! ! ! 'tt_news' => null, ! ! ! ! ! 'backPid' => null, ! ! ! ! ! $this->config['singleViewPointerName'] => null, ! ! ! ! ! 'pS' => null, ! ! ! ! ! 'pL' => null), $this->allowCaching, ($this->conf['dontUseBackPid']?1:0), $this->config['backPid']));
  • 32. 'pointer'; ! ! ! ! $wrappedSubpartArray['# ##LINK_ITEM###'] = explode('|', $this- >pi_linkTP_keepPIvars(' |', array( ! ! ! ! ! 'tt_news' => null, ! ! ! ! ! 'backPid' => null, ! ! ! ! ! $this- >config['singleViewPoin terName'] => null, ! ! ! ! ! 'pS' => null, ! ! ! ! ! 'pL' => null), $this- >allowCaching, ($this- >conf['dontUseBackPid'] ?1:0), $this- >config['backPid'])); ! ! ! } else { ! ! ! ! $wrappedSubpartArray['# ##LINK_ITEM###'] = explode('|', $this- >pi_linkTP_keepPIvars(' |', array( ! ! ! ! ! 'tt_news' => null, ! ! ! ! ! 'backPid' => null, ! ! ! ! ! $this- >config['singleViewPoin terName'] => null), $this->allowCaching, ($this- >conf['dontUseBackPid'] ?1:0), $this- >config['backPid'])); ! ! ! } ! ! ! ! // set the title of the single view page to the title of the news record ! ! ! if ($this- >conf['substitutePageti tle']) { ! ! ! ! $GLOBALS['TSFE']- >page['title'] = $row['title']; ! ! ! ! // set pagetitle for indexed search to news title ! ! ! ! $GLOBALS['TSFE']- >indexedDocTitle = $row['title']; ! ! ! } ! ! ! if ($this- >conf['displaySingle.'] ['catOrderBy']) { ! ! ! ! $this- >config['catOrderBy'] = $this- >conf['displaySingle.'] ['catOrderBy']; ! ! ! } ! ! ! $markerArray = $this- >getItemMarkerArray($ro w, 'displaySingle'); ! ! ! // Substitute ! ! ! $content = $this->cObj- >substituteMarkerArrayC ached($item, $markerArray, array(), $wrappedSubpartArray); ! ! } elseif ($this- >sys_language_mode == 'strict' && $this- >tt_news_uid && $GLOBALS['TSFE']- >sys_language_content) { // not existing translation ! ! ! $noTranslMsg = $this- >local_cObj- >stdWrap($this- >pi_getLL('noTranslMsg' ), $this- >conf['noNewsIdMsg_stdW rap.']); ! ! ! $content = $noTranslMsg; ! ! } elseif ($row['pid'] < 0) { // a non-public version of a record was requested ! ! ! $nonPlublicVersion = $this->local_cObj- >stdWrap($this- >pi_getLL('nonPlublicVe rsionMsg'), $this- >conf['nonPlublicVersio nMsg_stdWrap.']); ! ! ! $content = $nonPlublicVersion; ! ! } else { // if singleview is shown with no tt_news uid given from GETvars (&tx_ttnews[tt_news]=) an error message is displayed. ! ! ! $noNewsIdMsg = $this- >local_cObj- >stdWrap($this- >pi_getLL('noNewsIdMsg' ), $this-
  • 34. control flow business logic CRUD data render output other , eg. config
  • 35. control flow business logic aggregate CRUD data render output other , eg. config
  • 36. control flow business logic generalize CRUD data render output other , eg. config
  • 38. Extbase use & Fluid
  • 40. control flow business logic CRUD data render output other , eg. config
  • 42. Blog features administrate blogs, blog posts and blog comments list all available blogs list all blog posts of a blog list all comments of a blog post allow users to post new comments
  • 43. Blog features administrate blogs, blog posts and blog comments Blog list all available blogs list all blog posts of a blog list all comments of a blog post allow users to post new comments
  • 44. Blog features administrate blogs, blog posts and blog comments Blog list all available blogs list all blog posts of a blog list all comments of a blog post allow users to post new comments Post Comment Tag
  • 45. TYPO3
  • 46. tslib_piBase tx_blog 1 userFunc TYPO3 tx_blog_pi
  • 47. tslib_piBase tx_blog 1 userFunc 2 exec_SELECTgetRows TYPO3 tx_blog_pi Database
  • 48. tslib_piBase tx_blog 1 userFunc 2 exec_SELECTgetRows TYPO3 tx_blog_pi rows as Database array 3
  • 49. tslib_piBase tx_blog 1 userFunc 2 exec_SELECTgetRows TYPO3 tx_blog_pi HTML rows as Database 4 array 3
  • 50. Control ow public function main($content, $conf) { ! $this->conf = $conf; ! $this->pi_setPiVarDefaults(); ! $this->pi_loadLL(); ! if ($this->piVars['postUid']) { ! ! if ($this->piVars['newComment']) { ! ! ! $this->storeNewComment(); ! ! } ! ! $content = $this->renderPost(); ! } elseif ($this->piVars['blogUid']) { ! ! $content = $this->renderBlog(); ! } else { ! ! $content = $this->renderListOfBlogs(); ! } ! return $this->pi_wrapInBaseClass($content); }
  • 51. Task 1: Output a listing of blogs protected function renderListOfBlogs() { ! $blogs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! '*', ! ! 'tx_blogexample_blog', ! ! 'sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid . $this->cObj->enableFields('tx_blogexample_blog'), ! ! '', ! ! 'date' ! ! ); ! $template = $this->cObj->fileResource($this->conf['template']); ! $blogElementSubpart = $this->cObj->getSubpart($template, '###SUBPART_BLOGELEMENT###');
  • 52. Task 1: Output a listing of blogs ! foreach ($blogs as $blog) { ! ! $linkParameters = array('blogUid' => $blog['uid']); ! ! $markers = array( ! ! ! '###BLOG_NAME###' => $blog['name'], ! ! ! '###BLOG_LOGO###' => $this->cImage('uploads/tx_blog/' . $blog['logo']), ! ! ! '###BLOG_DESCRIPTION###' => $this->pi_RTEcssText($blog['description']), ! ! ! '###BLOG_MORELINK###' => $this->pi_linkTP('show blog', $linkParameters, true), ! ! ); ! ! $blogElements.= $this->cObj->substituteMarkerArray($blogElementSubpart, $markers); ! } ! return $content; }
  • 53. Task 1: Output a listing of blogs <!-- ###SUBPART_BLOGELEMENT### begin --> <div class="blog element"> ! ###BLOG_NAME### ! ###BLOG_LOGO### ! ###BLOG_DESCRIPTION### ! ###BLOG_MORELINK### </div> <!-- ###SUBPART_BLOGELEMENT### end -->
  • 54. Task 2: Display a single post with its comments protected function renderPost() { ! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']); ! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! '*', ! ! 'tx_blogexample_comment', ! ! 'deleted=0 AND hidden=0 AND sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid ! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"' . $this->cObj->enableFields('tx_blogexample_comment'), ! ! '', ! ! 'date DESC' ! ); // fill marker arrays and substitute in template // return content }
  • 55. Task 3: Add a new comment to a blog post the whole plugin is cached („USER“) dynamic user input won‘t outputted instantly de ne uncached behavior in TypoScript [globalVar = _POST:tx_blogexample_pi1|newComment = 1] ! plugin.tx_blogexample_pi1 = USER_INT [globals]
  • 56. Task 3: Add a new comment to a blog post protected function storeNewComment() { ! $fields = array( ! ! 'post_uid' => $this->piVars['postUid'], ! ! 'post_table' => 'tx_blogexample_post', ! ! 'date' => time(), ! ! 'author' => $this->piVars['author'], ! ! 'email' => $this->piVars['email'], ! ! 'content' => $this->piVars['content'], ! ); ! $GLOBALS['TYPO3_DB']->exec_INSERTquery( ! ! 'tx_blogexample_comment', $fields ! ); }
  • 57. Security: SQL injections protected function renderPost() { ! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']); ! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! '*', ! ! 'tx_blogexample_comment', ! ! 'deleted=0 AND hidden=0 AND sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid ! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"' . $this->cObj->enableFields('tx_blogexample_comment'), ! ! '', ! ! 'date DESC' ! ); [...]
  • 58. Security: SQL injections protected function renderPost() { ! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']); ! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! '*', ! ! 'tx_blogexample_comment', ! ! 'deleted=0 AND hidden=0 AND sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid ! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"' . $this->cObj->enableFields('tx_blogexample_comment'), ! ! '', ! ! 'date DESC' ! ); [...] http://www.example.com/index.php&id=99&postUid=1
  • 59. Security: SQL injections protected function renderPost() { ! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']); ! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! '*', ! ! 'tx_blogexample_comment', ! ! 'deleted=0 AND hidden=0 AND sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid ! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"' . $this->cObj->enableFields('tx_blogexample_comment'), ! ! '', ! ! 'date DESC' ! ); [...] http://www.example.com/index.php&id=99&postUid=1 SELECT * FROM tx_blog_comment WHERE post_uid=1;
  • 60. Security: SQL injections protected function renderPost() { ! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']); ! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! '*', ! ! 'tx_blogexample_comment', ! ! 'deleted=0 AND hidden=0 AND sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid ! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"' . $this->cObj->enableFields('tx_blogexample_comment'), ! ! '', ! ! 'date DESC' ! ); [...] http://www.example.com/index.php&id=99&postUid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1 SELECT * FROM tx_blog_comment WHERE post_uid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1 AND post_table="tx_blogexample_post"
  • 61. Security: SQL injections protected function renderPost() { ! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']); ! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! '*', ! ! 'tx_blogexample_comment', ! ! 'deleted=0 AND hidden=0 AND sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid ! ! ! . ' AND post_uid=' . $this->piVars['postUid'] . ' AND post_table="tx_blogexample_post"' . $this->cObj->enableFields('tx_blogexample_comment'), ! ! '', ! ! 'date DESC' ! ); [...] http://www.example.com/index.php&id=99&postUid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1 SELECT * FROM tx_blog_comment WHERE post_uid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1 AND post_table="tx_blogexample_post"
  • 62. Security: SQL injections protected function renderPost() { ! $post = $this->pi_getRecord('tx_blogexample_post', $this->piVars['postUid']); ! $comments = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! '*', ! ! 'tx_blogexample_comment', ! ! 'deleted=0 AND hidden=0 AND sys_language_uid= . $GLOBALS['TSFE']->sys_language_uid ! ! ! . ' AND post_uid=' . intval($this->piVars['postUid']) . ' AND post_table="tx_blogexample_post"' . $this->cObj->enableFields('tx_blogexample_comment'), ! ! '', ! ! 'date DESC' ! ); [...] http://www.example.com/index.php&id=99&postUid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1 SELECT * FROM tx_blog_comment WHERE post_uid=1; INSERT INTO be_users SET ...; SELECT * FROM tx_blog_comment WHERE 1=1 AND post_table="tx_blogexample_post"
  • 63. http://www.sxc.hu/photo/516864/ Extension building with Extbase
  • 64. TYPO3
  • 65. 1 userFunc Extbase TYPO3 Dispatcher
  • 66. 1 2 userFunc Request BlogExample Extbase TYPO3 Dispatcher Controller
  • 67. 1 2 userFunc Request BlogExample Extbase TYPO3 Dispatcher Controller 3 findByTitle('MyBlog') Repository Domain Model Blog Post Comment Tag
  • 68. 1 2 userFunc Request BlogExample Extbase TYPO3 Dispatcher Controller 3 findByTitle('MyBlog') Blog 4 Repository Domain Model Blog Post Comment Tag
  • 69. 1 2 userFunc Request BlogExample Extbase TYPO3 Dispatcher Controller 3 assign(Blog) findByTitle('MyBlog') render() 5 Blog 4 View Repository Domain Model Blog Post Comment Tag
  • 70. 1 2 userFunc Request BlogExample Extbase TYPO3 Dispatcher HTML Response Controller 6 3 assign(Blog) findByTitle('MyBlog') render() 5 Blog Response 4 View Repository Domain Model Blog Post Comment Tag
  • 71. 1 2 userFunc Request BlogExample Extbase TYPO3 Dispatcher HTML Response Controller 6 3 assign(Blog) findByTitle('MyBlog') render() 5 Blog Response 4 View Repository Domain Model Blog Post Comment Tag
  • 72. Blog Post Comment Tag
  • 73. Model
  • 74. /** * A blog * * @version $Id:$ * @copyright Copyright belongs to the respective authors * @license http://opensource.org/licenses/gpl-license.php GNU Public License, version 2 * @scope prototype * @entity */ class Tx_BlogExample_Domain_Model_Blog extends Tx_Extbase_DomainObject_AbstractEntity { ! /** ! * The blog's title. ! * ! * @var string ! * @validate Text, StringLength(minimum = 1, maximum = 80) ! * @identity ! */ ! protected $title = ''; ! /** ! * A short description of the blog ! * ! * @var string ! * @validate Text, StringLength(maximum = 150) ! */ ! protected $description = '';
  • 75. ! } ! /** ! * Sets this blog's title ! * ! * @param string $title The blog's title ! * @return void ! */ ! public function setTitle($title) { ! ! $this->title = $title; ! } ! /** ! * Returns the blog's title ! * ! * @return string The blog's title ! */ ! public function getTitle() { ! ! return $this->title; ! } ! /** ! * Sets the description for the blog ! * ! * @param string $description ! * @return void ! */ ! public function setDescription($description) { ! ! $this->description = $description;
  • 76. ! * @return Tx_Extbase_Persistence_ObjectStorage ! */ ! public function getPosts() { ! ! return $this->posts; ! } ! /** ! * Sets the administrator value ! * ! * @param Tx_BlogExample_Domain_Model_Administrator $administrator The Administrator of this Blog ! * @return void ! */ ! public function setAdministrator(Tx_BlogExample_Domain_Model_Administrator $administrator) { ! ! $this->administrator = $administrator; ! } ! /** ! * Returns the administrator value ! * ! * @return Tx_BlogExample_Domain_Model_Administrator ! */ ! public function getAdministrator() { ! ! return $this->administrator; ! } }
  • 77. /** * A blog post comment * * @package Blog * @subpackage Domain * @version $Id:$ * @copyright Copyright belongs to the respective authors * @license http://opensource.org/licenses/gpl-license.php GNU Public License, version 2 * @scope prototype * @entity */ class Tx_BlogExample_Domain_Model_Comment extends Tx_Extbase_DomainObject_AbstractEntity { ! /** ! * @var DateTime ! */ ! protected $date; ! /** ! * @var string ! * @validate Text, StringLength(minimum = 3, maximum = 80) ! */ ! protected $author; ! /** ! * @var string ! * @validate EmailAddress
  • 78. ! * @param string $content ! * @return void ! */ ! public function setContent($content) { ! ! $this->content = $content; ! } ! /** ! * Getter for content ! * ! * @return string ! */ ! public function getContent() { ! ! return $this->content; ! } ! /** ! * Returns this comment as a formatted string ! * ! * @return string ! */ ! public function __toString() { ! ! return $this->author . ' (' . $this->email . ') said on ' . $this->date->format('Y-m-d') . ':' . chr(10) ! ! ! $this->content . chr(10); ! } }
  • 80. /** * The blog controller for the Blog package * * @version $Id:$ * @license http://opensource.org/licenses/gpl-license.php GNU Public License, version 2 */ class Tx_BlogExample_Controller_BlogController extends Tx_Extbase_MVC_Controller_ActionController { ! /** ! * @var Tx_BlogExample_Domain_Model_BlogRepository ! */ ! protected $blogRepository; ! /** ! * Initializes the current action ! * ! * @return void ! */ ! public function initializeAction() { ! ! $this->blogRepository = t3lib_div::makeInstance('Tx_BlogExample_Domain_Repository_BlogRepository'); ! ! $this->postRepository = t3lib_div::makeInstance('Tx_BlogExample_Domain_Repository_PostRepository'); ! ! $this->administratorRepository = t3lib_div::makeInstance('Tx_BlogExample_Domain_Repository_AdministratorRepository'); ! } ! /** ! * Index action for this controller. Displays a list of blogs. ! * ! * @return string The rendered view ! */ ! public function indexAction() { ! ! $this->view->assign('blogs', $this->blogRepository->findAll()); ! }
  • 81. ! } ! /** ! * Index action for this controller. Displays a list of blogs. ! * ! * @return string The rendered view ! */ ! public function indexAction() { ! ! $this->view->assign('blogs', $this->blogRepository->findAll()); ! } ! /** ! * Displays a form for creating a new blog ! * ! * @param Tx_BlogExample_Domain_Model_Blog $newBlog A fresh blog object taken as a basis for the rendering ! * @return string An HTML form for creating a new blog ! * @dontvalidate $newBlog ! */ ! public function newAction(Tx_BlogExample_Domain_Model_Blog $newBlog = NULL) { ! ! $this->view->assign('newBlog', $newBlog); ! ! $this->view->assign('administrators', $this->administratorRepository->findAll()); ! } ! /** ! * Creates a new blog ! * ! * @param Tx_BlogExample_Domain_Model_Blog $newBlog A fresh Blog object which has not yet been added to the repository ! * @return void ! */ ! public function createAction(Tx_BlogExample_Domain_Model_Blog $newBlog) { ! ! $this->blogRepository->add($newBlog); ! ! $this->pushFlashMessage('Your new blog was created.'); ! ! $this->redirect('index'); ! }
  • 84. Model Blog Post Comment Tag
  • 85. Model BlogRepository Blog Post Comment Tag
  • 86. /** * A repository for Blogs */ class Tx_BlogExample_Domain_Repository_BlogRepository extends Tx_Extbase_Persistence_Repository { ! ! /** ! * Remove the blog's posts before removing the blog itself. ! * ! * @return array An array filled with blogs ! */ ! public function findAll() { ! ! $blogs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( ! ! ! '*', ! ! ! 'tx_blogexample_domain_model_blog', ! ! ! 'sys_language_uid=' . $GLOBALS['TSFE']->sys_language_uid . $this->cObj->enableFields('tx_blogexample_domain_model_blog'), ! ! ! '', ! ! ! 'date' ! ! ! ); ! ! ! ! ! return $blogs; ! } }
  • 87. /** * A repository for Blogs */ class Tx_BlogExample_Domain_Repository_BlogRepository extends Tx_Extbase_Persistence_Repository { ! }
  • 88. /** * A repository for Posts */ class Tx_BlogExample_Domain_Repository_PostRepository extends Tx_Extbase_Persistence_Repository { ! ! /** ! * Finds posts by the specified blog ! * ! * @param Tx_BlogExample_Domain_Model_Blog $blog The blog the post must refer to ! * @param integer $limit The number of posts to return at max ! * @return array The posts ! */ ! public function findByBlog(Tx_BlogExample_Domain_Model_Blog $blog, $limit = 20) { ! ! $query = $this->createQuery(); ! ! return $query->matching($query->equals('blog', $blog)) ! ! ! ->setOrderings(array('date' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING)) ! ! ! ->setLimit($limit) ! ! ! ->execute(); ! } ! /** ! * Finds the previous of the given post ! * ! * @param Tx_BlogExample_Domain_Model_Post $post The reference post ! * @return Tx_BlogExample_Domain_Model_Post ! */ ! public function findPrevious(Tx_BlogExample_Domain_Model_Post $post) {
  • 89. ! * @param Tx_BlogExample_Domain_Model_Post $post The reference post ! * @return Tx_BlogExample_Domain_Model_Post ! */ ! public function findNext(Tx_BlogExample_Domain_Model_Post $post) { ! ! $query = $this->createQuery(); ! ! $posts = $query->matching($query->greaterThan('date', $post->getDate())) ! ! ! ->setOrderings(array('date' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING)) ! ! ! ->setLimit(1) ! ! ! ->execute(); ! ! return (count($posts) == 0) ? NULL : current($posts); ! } ! /** ! * Finds most recent posts by the specified blog ! * ! * @param Tx_BlogExample_Domain_Model_Blog $blog The blog the post must refer to ! * @param integer $limit The number of posts to return at max ! * @return array The posts ! */ ! public function findRecentByBlog(Tx_BlogExample_Domain_Model_Blog $blog, $limit = 5) { ! ! $query = $this->createQuery(); ! ! return $query->matching($query->equals('blog', $blog)) ! ! ! ->setOrderings(array('date' => Tx_Extbase_Persistence_QueryInterface::ORDER_DESCENDING)) ! ! ! ->setLimit($limit) ! ! ! ->execute(); ! } }
  • 90. Repositories Encapsulate all data access SQL is allowed only in the Repository ... but not necessary anymore: use the Query object instead Magic methods: ndBy*, ndOneBy*
  • 91. Blog Post Comment Tag
  • 92. Aggregate Blog Post Comment Tag
  • 93. Blog Entity Post Comment Tag
  • 94. Blog Entity Post Value Object Comment Tag
  • 95. Aggregate Root Blog Entity Post Value Object Comment Tag
  • 96. Aggregate getLatestComment() Root Blog Post Comment Tag
  • 97. Aggregate getLatestComment() Root Blog Post Comment Tag
  • 98. Aggregate getLatestComment() Root Blog Post Comment Tag
  • 99. <h1 class="csc-firstHeader">Welcome to {blog.title}</h1> <p class="bodytext">{blog.description}</p> <div class="tx-blogexample-list-container"> ! <f:if condition="{blog.posts}"> ! ! <f:then> ! ! <p class="bodytext"><f:translate key="label_recent_posts">Below are the most recent posts:</f:translate></p> ! ! ! <ul> ! ! ! ! <f:for each="{blog.posts}" as="post"> ! ! ! ! ! <li> ! ! ! ! ! ! <h3> ! ! ! ! ! ! ! <f:format.date>{post.date}</f:format.date> ! ! ! ! ! ! ! <f:link.action action="show" controller="Post" arguments="{post: post, blog: post.blog}">{post.title}</f:link.action> ! ! ! ! ! ! </h3> ! ! ! ! ! ! <p class="bodytext"><f:format.crop maxCharacters="100">{post.content}</f:format.crop></p> ! ! ! ! ! ! <p> ! ! ! ! ! ! ! By: {post.author.fullName}<br /> ! ! ! ! ! ! ! Tags: <f:for each="{post.tags}" as="tag">[{tag.name}]&nbsp;</f:for><br /> ! ! ! ! ! ! ! <f:link.action controller="Post" action="show" arguments="{post : post}"> <f:translate key="read_more">read more &gt;&gt;</f:translate> </f:link.action><br /> ! ! ! ! ! ! ! <f:link.action controller="Post" action="edit" arguments="{post : post, blog : blog}"> Edit </f:link.action>&nbsp; <f:link.action controller="Post" action="delete" arguments="{post : post, blog : blog}"> Delete </f:link.action> ! ! ! ! ! ! </p> ! ! ! ! ! </li> ! ! ! ! </f:for> ! ! ! </ul> ! ! </f:then> ! ! <f:else> ! ! ! <p>This blog currently doesn't contain any posts.</p> ! ! </f:else> ! </f:if> ! <p><f:link.action action="new" controller="Post" arguments="{blog : blog}">Create a new Post</f:link.action></p> </div>
  • 100. Security All arguments must be registered. Registration of expected arguments happens through de ning them as method parameters. PHPDoc is mandatory as it is used for data type validation
  • 101. */ class Tx_BlogExample_Domain_Model_Comment extends Tx_Extbase_DomainObject_AbstractEntity { ! /** ! * @var DateTime ! */ ! protected $date; ! /** ! * @var string ! * @validate Text, StringLength(minimum = 3, maximum = 80) ! */ ! protected $author; ! /** ! * @var string ! * @validate EmailAddress ! */ ! protected $email; ! /** ! * @var string ! * @validate Text, NotEmpty ! */ ! protected $content; ! ! ! /** ! * Constructs this post
  • 102. Controlle r odel Do main / M View Con fig ura tio n
  • 103. Principles of Domain Driven Design focus on the domain = activity or business of user we start with the business logic (PHP classes) we don't care about the database backend / persistence layer objects represent things in the real world, with their attributes and behavior ubiquitous language building blocks Entity Value Objects Repositories
  • 105. What's next New Kickstarter currently ongoing project by the core development team will be released shortly after 4.3 Domain-Driven Design - Don't think in databases, think in Domain Models! Support for BE-Modules experimental by now AJAX-Dispatcher Dependency Injection PDO Storage Backend
  • 107. - Modern architecture
  • 113. Bastian Waidelich Sebastian Kurfürst Than k You Steffen Kamper and the TYPO3 V5 Team for all the inspiration and the beautiful code Ingmar Schlecht Christopher Hlubek Xavier Perseguers
  • 114. Resources and links Project web site: http://forge.typo3.org/projects/show/typo3v4-mvc SVN: https://svn.typo3.org/TYPO3v4/CoreProjects/MVC/ Newslist typo3.projects.typo3v4mvc@lists.net elders.de First stable release with TYPO3 4.3 alpha3: http://typo3.org/download/packages/