The step in which we abstract a part of the real world (the "domain") into a representation within the extension is called "domain modelling" and is, according to "Reliable Extensions", the most important part of extension development. The book uses the "sjr_offers" extension as an example of how to model a real-world domain: namely, this extension was developed for the "Stadtjugendring Stuttgart e.V." (Youth Organisation Ring Stuttgart). Whereas yesterday we developed a simple extension with the help of Extension Builder, today we're going to model the example domain by hand, using the principles of test-driven-development by writing PHPUnit tests before implementing functionality. Whee!
Looking at the list of desired functionalities, I sketched out a primary list of actions and from this list, gleaned a bunch of terms I thought might be part of the domain models. I was mostly right - yeah! Luckily, though the book (and the example domain) is in German, extensions are developed in English and I don't have to translate back and forth.
I then sketched a primitve design of how a few of the models are associated with each other - again, there was a pretty good match between what I had supposed to be the model associations and what the book revealed. This is only because the book also started with a very basic model and then refined it, step by step.
In the process, I learned a useful trick for abstracting pair-properties within a model. For example, if we have the properties "minimum age" and "maximum age", "minimum participant number" and "maximum particpant number" and "start date" and "end date", these pairs have their own rules that only apply to the two properties within the pair, and not necessarily to their aggregate root model (in this case, "activity offer"). Therefore, these pair properties are all extracted to their own models: ParticipationRange, AgeRange, and DateRange.
Furthermore, each of these new models have something in common that can be further abstracted, and that is a minimum value and a maximum value. "Reliable Extensions" extracted these generic properties into their own domain model: RangeConstraint. The models for AgeRange, ParticipantRange and DateRange are now associated with the RangeConstraint model.
This is an incredibly useful tip - this is exactly the kind of thing I want to learn from people who have more experience working on complex (real-world) projects. Thanks, @Sebastien Kurürst and @Martin Helmich!
On to implementation. My questions from yesterday, about how to take a test-driven approach to extension development, were answered neatly today. There are two ways to implement a domain-model: the "top-to-bottom" approach starts with aggregate root objects, like "offer" or "organization", that "contain" other models. The "bottom-to-top" approach does the opposite and starts with the elementary objects contained with the aggregate root. The approach you decide to use will have an effect on how tests are written - "Reliable Extensions" paves the way with the top-to-bottom approach, tackling the big ol' "organization" model first.
Yesterday, basic domain models were implemented as PHP classes for us by the Extension Builder and saved in the "Classes/Domain/Model" directory. Today, we create these classes by hand. Instead of, however, starting with "Organization.php", we will start with "OrganizationTest.php", which we will create in the Tests/Domain/Model" directory.
I decided to bite the bullet and start actually using Eclipse to develop. Following the instructions from last week, I started a new project, "SrjOffers" and linked the PHP paths "Extbase", "Fluid" and "PHPUnit" to my new project in order to set the basis for auto-completion. In order to do this, you need to make sure that the owners and permissions for the root directories of these libraries are correct.
"Reliable Extensions" recommends that the structure of the "Tests" directory should mirror that of the "Classes" directory, in order to keep everything clearly arranged as the number of unit-tests grows. I'm wondering, though, if I should start with "Tests/Unit/Domain/Model" instead of their recommended structure "Tests/Domain/Model", to make room for other kinds of tests later, like integration and acceptence testing.
So, the first unit test is written... how do I run it in Eclipse? I know how to run unit tests from the command line and from within the TYPO3 backend. However, since we're writing this extension by hand, it is not yet an official "extension" in TYPO3 and therefore cannot be connected with unit tests. Hello again, my friend Google. "PHPhatesme.com" was an excellent source of information for this question:
Under "Help" -> "Install new software", click on the "Add" button and enter "http://www.phpsrc.org/eclipse/pti-dev/" as URL and "PHP Tool Integration" as the name. Now, select this site from the dropdown and open up the lists that appear in the select box: Core, Library, and Tools. Select the PTI core under "Core", PEAR under "Library" and "PHPUnit" under "Tools". You know what's really cool? Under "Tools" you can also pick PHP Code Sniffer, PHP Copy-and-Paste detector, PHP Mess Detector, and PHP Depend, if you want to.
Eclipse will install your selections and then you have to restart. Now, when I right-click on my OrganizationTest.php file in the project tree, I can select from the menu "PHP Tools" -> "PHP Unit" -> "Run Unit Test". Unfortunately, what this produces in the console is "No case / test suite found for file OrganizationTest.php". I'm guessing that I have to write a bootstrap file? I wrote to the forum on the article on "phphatesme" - because, PHP does hate me, apparently.
Despite all of our difficulties, PHP, we've been through so much together, and after 15 years of ups and downs I'm obviously still willing to work on our relationship, 'till death do us part!
I have two specific problems now: 1) why isn't Eclipse auto-completing the code I write in the class inheriting from \TYPO3\CMS\Extbase\Tests\Unit? and 2) why can't PHPUnit find my class to test? Both seem to be related, because the error message I get when running PHPUnit on the command line is that "Class TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase not found".
Problem 1: with the help of two co-hackers we saw, first of all, that I had typos in my name-space and directory-name (eye-roll). Fixed these, deleted the project, re-imported the correctly-named project in Eclipse, and tried again. Same error. With some more help and prodigious googling, we came up with the following solution by @Carsten König:
Bind TYPO3 as a library in Eclipse - for autocompletion on all Typo3 libraries
- Open "Window"->"Preferences".
- Select "PHP"->"PHP Libraries" -> "New...".
- A window titled "User Library" will pop up prompting you to enter a name. Type the name "TYPO3" and click "OK".
- Select the new entry, "TYPO3" and then "Add external folder".
- Browse for the "typo3_src" file - on my system, it was in "\var\www\buch\html\typo3_src", select it, and click "OK".
- Right-click on project that should have auto-completion. Select "Properties" -> "PHP Include Path" and click on the "Libraries" tab.
- Choose "Add Library" and check the box next to "TYPO3". Click "Finish" and then "OK".
This worked in that writing a test class that extends the TYPO3 BaseTestCase, which in turn extends the TYPO3 Core UnitTestCase, calling "$this" gives me a long list of methods from PHPUnit to choose from. I still get the same error running the unit test in Eclipse, though (and a probably related one on the command line).
Running PHPUnit on one of the files "without detection" gives me the error "PHP Fatal error: Call to undefined method PHP_CodeCoverage_Filter::getInstance() in /home/kaiser/programming/zend-eclipse-php/plugins/org.phpsrc.eclipse.pti.tools.phpunit_0.7.2.R201106270000000/php/tools/phpunit.php on line 40".
I report this after 2 hours of searching for an answer. The command line error remains "can not find (TYPO3 BaseTestCase) class". Enough for now, though; I've been working for nearly 9 hours and it's time to take a break.
To be continued, friends (and enemies) of Typo3...