1
|
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
<chapter version="5.0" xml:id="flow3.mvcframework"
|
3
|
xmlns="http://docbook.org/ns/docbook"
|
4
|
xmlns:ns52="http://www.w3.org/1998/Math/MathML"
|
5
|
xmlns:ns5="http://www.w3.org/1999/xlink"
|
6
|
xmlns:ns4="http://www.w3.org/2000/svg"
|
7
|
xmlns:ns3="http://www.w3.org/1999/xhtml"
|
8
|
xmlns:ns="http://docbook.org/ns/docbook">
|
9
|
<title>MVC Framework</title>
|
10
|
|
11
|
<section xml:id="flow3.mvcframework.introduction">
|
12
|
<title>Introduction</title>
|
13
|
|
14
|
<section>
|
15
|
<title>Model-View-Controller</title>
|
16
|
|
17
|
<para>In the design of FLOW3's architecture we have taken great care to
|
18
|
separate concerns and assign each part of the framework with
|
19
|
well-defined tasks. The separation of concerns is an important principle
|
20
|
of good software design and its most prominent representative probably
|
21
|
is the Model-View-Controller pattern. MVC separates the business logic
|
22
|
from the presentation by splitting up user interaction into three
|
23
|
roles:</para>
|
24
|
|
25
|
<itemizedlist>
|
26
|
<listitem>
|
27
|
<para>The <emphasis>model</emphasis> is an object which contains
|
28
|
data and business logic of a certain domain. It doesn't contain any
|
29
|
information about the presentation of that data, but rather defines
|
30
|
the behaviour. In the FLOW3 project we prefer a special kind of
|
31
|
model, the <link
|
32
|
ns5:href="http://martinfowler.com/eaaCatalog/domainModel.html">Domain
|
33
|
Model</link>.</para>
|
34
|
</listitem>
|
35
|
|
36
|
<listitem>
|
37
|
<para>The <emphasis>view</emphasis> represents the display of the
|
38
|
model on the web or another output channel. Views only display data,
|
39
|
they don't build or modify it.</para>
|
40
|
</listitem>
|
41
|
|
42
|
<listitem>
|
43
|
<para>The <emphasis>controller</emphasis> reacts on user input,
|
44
|
selects and manipulates the model as accordingly, selects a view and
|
45
|
passes it the prepared model for rendering.</para>
|
46
|
</listitem>
|
47
|
</itemizedlist>
|
48
|
|
49
|
<para>This diagram outlines the collaboration between model, view and
|
50
|
controller:</para>
|
51
|
|
52
|
<figure>
|
53
|
<title>Model-View-Controller Pattern</title>
|
54
|
|
55
|
<mediaobject>
|
56
|
<imageobject>
|
57
|
<imagedata contentdepth="100%"
|
58
|
fileref="MVC_ModelViewController.png"
|
59
|
scalefit="1" width="100%"/>
|
60
|
</imageobject>
|
61
|
</mediaobject>
|
62
|
</figure>
|
63
|
</section>
|
64
|
|
65
|
<section>
|
66
|
<title>Other Patterns Used</title>
|
67
|
|
68
|
<para>Design Patterns (and MVC is one of them) are not only great for
|
69
|
solving reoccuring design problems in a structured manner - they also
|
70
|
help you communicating software designs. The following patterns play an
|
71
|
important role in FLOW3's MVC mechanism and might give you a better idea
|
72
|
of the overall design:</para>
|
73
|
|
74
|
<itemizedlist>
|
75
|
<listitem>
|
76
|
<para>Incoming requests are handled by a Request Handler which takes
|
77
|
the role of a <link ns5:href="???">Front Controller</link>.</para>
|
78
|
</listitem>
|
79
|
|
80
|
<listitem>
|
81
|
<para><link ns5:href="???">Template View</link> is the most commonly
|
82
|
used pattern for views, but <link ns5:href="???">Transform
|
83
|
Views</link> and <link ns5:href="???">Two-Step Views</link> are
|
84
|
equally supported.</para>
|
85
|
</listitem>
|
86
|
|
87
|
<listitem>
|
88
|
<para>The preferred type of model is the <link ns5:href="???">Domain
|
89
|
Model</link>.</para>
|
90
|
</listitem>
|
91
|
</itemizedlist>
|
92
|
</section>
|
93
|
|
94
|
<section>
|
95
|
<title>Hello World!</title>
|
96
|
|
97
|
<para>Let's start with an example before we go into greater detail of
|
98
|
request handling and the internals of the MVC framework. The minimal
|
99
|
approach is to create an Action Controller which just returns
|
100
|
<quote>Hello World!</quote>. To begin with, we need to create some
|
101
|
directories which contain the code of our FLOW3 package and eventually
|
102
|
the controller class:</para>
|
103
|
|
104
|
<literallayout>Packages/
|
105
|
Demo/
|
106
|
Classes/
|
107
|
Controller/
|
108
|
StandardController.php</literallayout>
|
109
|
|
110
|
<para>The StandardController class looks as simple as this (leaving out
|
111
|
the very recommended comments):</para>
|
112
|
|
113
|
<example>
|
114
|
<title>Hello World! controller</title>
|
115
|
|
116
|
<programlisting language="php">namespace TYPO3\Demo\Controller;
|
117
|
|
118
|
class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
|
119
|
public function indexAction() {
|
120
|
return 'Hello World!';
|
121
|
}
|
122
|
}</programlisting>
|
123
|
</example>
|
124
|
|
125
|
<para>Provided that the document root of your local server points to
|
126
|
FLOW3's <filename>Web/</filename> directory, you will get the following
|
127
|
output when calling the URI <uri>http://localhost/demo/</uri>:</para>
|
128
|
|
129
|
<screen>Hello World!</screen>
|
130
|
|
131
|
<para>Great, that was easy - but didn't we say that it's the view's
|
132
|
responsibility to take care of the presentation? Let's create a simple
|
133
|
PHP-based view for that purpose:</para>
|
134
|
|
135
|
<literallayout>Packages/
|
136
|
Demo/
|
137
|
Classes/
|
138
|
Controller/
|
139
|
StandardController.php
|
140
|
View/
|
141
|
Standard/
|
142
|
Index.php</literallayout>
|
143
|
|
144
|
<para>The view's code is equally trivial:</para>
|
145
|
|
146
|
<example>
|
147
|
<title>Hello World! view</title>
|
148
|
|
149
|
<programlisting language="php">namespace TYPO3\Demo\View\Standard;
|
150
|
|
151
|
class Index extends \TYPO3\FLOW3\MVC\View\AbstractView {
|
152
|
public function render() {
|
153
|
return 'Hello World from your view!';
|
154
|
}
|
155
|
}</programlisting>
|
156
|
</example>
|
157
|
|
158
|
<para>Finally our action controller needs a little tweak to return the
|
159
|
rendered view instead of shouting <quote>Hello World!</quote>
|
160
|
itself:</para>
|
161
|
|
162
|
<example>
|
163
|
<title>Improved Hello World! controller</title>
|
164
|
|
165
|
<programlisting language="php">namespace TYPO3\Demo\Controller;
|
166
|
|
167
|
class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
|
168
|
public function indexAction() {
|
169
|
return $this->view->render();
|
170
|
}
|
171
|
}</programlisting>
|
172
|
</example>
|
173
|
|
174
|
<para>Some notes about the view: Although a view class written in PHP is
|
175
|
the most basic way to implement a view, it is not the most common way
|
176
|
you'll take. In practice you'll want to use <package>Fluid</package>,
|
177
|
FLOW3's powerful templating engine which allows you to write views in
|
178
|
plain HTML and still have all the power like loops and
|
179
|
conditions.</para>
|
180
|
</section>
|
181
|
|
182
|
<section>
|
183
|
<title>Recommended File Structure</title>
|
184
|
|
185
|
<para>As you have seen in the hello world example, conventions for the
|
186
|
directory layout simplify your development a lot. There's no need to
|
187
|
register controllers, actions or views if you follow our recommended
|
188
|
file structure. These are the rules:</para>
|
189
|
|
190
|
<itemizedlist>
|
191
|
<listitem>
|
192
|
<para><emphasis>Controllers</emphasis> are located in their own
|
193
|
directory <filename>Controller</filename> just below the
|
194
|
<filename>Classes</filename> directory of your package. They can
|
195
|
have arbitrary names while the
|
196
|
<classname>StandardController</classname> has a special meaning: If
|
197
|
the package was specified in the request but no controller, the
|
198
|
<classname>StandardController</classname> will be used.</para>
|
199
|
</listitem>
|
200
|
|
201
|
<listitem>
|
202
|
<para><emphasis>View</emphasis> classes are situated below a
|
203
|
<filename>View</filename> directory. The classname of the view is a
|
204
|
combination of the name of the controller and the name of the
|
205
|
action.</para>
|
206
|
</listitem>
|
207
|
</itemizedlist>
|
208
|
|
209
|
<para>This sample directory layout demonstrates the above rules:</para>
|
210
|
|
211
|
<example>
|
212
|
<title>Sample file structure</title>
|
213
|
|
214
|
<literallayout>Packages/
|
215
|
Demo/
|
216
|
Classes/
|
217
|
Controller/
|
218
|
StandardController.php
|
219
|
CustomerController.php
|
220
|
OrderController.php
|
221
|
View/
|
222
|
Standard/
|
223
|
Index.php
|
224
|
Customer/
|
225
|
Index.php
|
226
|
List.php
|
227
|
Details.php
|
228
|
Order/
|
229
|
List.php</literallayout>
|
230
|
</example>
|
231
|
|
232
|
<para>Adhering to these conventions has the advantage that the classname
|
233
|
of the view for example is resolved automatically. However it is
|
234
|
possible (and not really difficult) to deviate from this layout and have
|
235
|
a completely different structure.</para>
|
236
|
</section>
|
237
|
|
238
|
<section>
|
239
|
<title>From the URI to the view</title>
|
240
|
|
241
|
<caution>
|
242
|
<para>For the example URIs we assume that the web root directory of
|
243
|
your local server points to FLOW3's <filename>public/</filename>
|
244
|
directory. If that's not the case you have to extend the URI
|
245
|
accordingly.</para>
|
246
|
</caution>
|
247
|
|
248
|
<para>FLOW3 provides a standard way of resolving the URI to your
|
249
|
MVC-Objects.</para>
|
250
|
|
251
|
<para>Say, you want to see the list of customers (based on the
|
252
|
file-structure-example above). The URI to get the list would be:
|
253
|
<uri>http://localhost/demo/customer/list.html</uri> or just
|
254
|
<uri>http://localhost/demo/customer/list</uri>.</para>
|
255
|
|
256
|
<para>This URI will be resolved into the package-name
|
257
|
(<emphasis>Demo</emphasis>), controller-name
|
258
|
(<emphasis>Customer</emphasis>), action-name(<emphasis>list</emphasis>)
|
259
|
and format-name (<emphasis>html</emphasis> - which is the default
|
260
|
format).</para>
|
261
|
|
262
|
<para>Depending on that, the controller
|
263
|
<classname>\TYPO3\Demo\Controller\CustomerController</classname> (pattern:
|
264
|
'<code>TYPO3\@package\Controller\@controllerController'</code>) and its
|
265
|
method <methodname>listAction()</methodname> will be used. The
|
266
|
corresponding view is <classname>\TYPO3\Demo\View\CustomerList</classname>
|
267
|
(Pattern:
|
268
|
<code>'TYPO3\@package\View\@controller@action@format'</code>).</para>
|
269
|
|
270
|
<para>By looking at the view pattern you easily see that it's fairly
|
271
|
easily to address a view which renders a different format, for example
|
272
|
XML. All you need to do is creating a class called
|
273
|
<classname>\TYPO3\Demo\View\Customer\ListXML</classname>. To see the output
|
274
|
of this new view, just use the URI
|
275
|
<uri>http://localhost/demo/customer/list.xml</uri>.</para>
|
276
|
</section>
|
277
|
</section>
|
278
|
|
279
|
<section xml:id="flow3.mvcframework.requestresponse">
|
280
|
<title>Request and Response</title>
|
281
|
|
282
|
<para>No matter if a FLOW3 application runs in a web context or is
|
283
|
launched from the command line, the basic workflow is always the same: The
|
284
|
user request is analyzed and forwarded to an appropriate controller which
|
285
|
decides on which actions to take and finally returns a response which is
|
286
|
handed over to the user. This section highlights the flow and the
|
287
|
collaborators in the request-response machinery.</para>
|
288
|
|
289
|
<section>
|
290
|
<title>Request Processing Overview</title>
|
291
|
|
292
|
<para>A sequence diagram is worth a thousand words said my grandma, so
|
293
|
let's take a look at the standard request-response workflow in
|
294
|
FLOW3:</para>
|
295
|
|
296
|
<figure>
|
297
|
<title>Example of a Web Request-Response Workflow</title>
|
298
|
|
299
|
<mediaobject>
|
300
|
<imageobject>
|
301
|
<imagedata contentdepth="100%"
|
302
|
fileref="MVC_RequestResponseWorkflow.png"
|
303
|
scalefit="1" width="100%"/>
|
304
|
</imageobject>
|
305
|
</mediaobject>
|
306
|
</figure>
|
307
|
|
308
|
<para>As you see, there are a lot of parts of the framework involved for
|
309
|
answering a request - and the diagram doesn't even consider caching or
|
310
|
forwarding of requests. But we didn't create this structure just for the
|
311
|
fun of it - each object plays an important role as you'll see in the
|
312
|
next sections.</para>
|
313
|
</section>
|
314
|
|
315
|
<section>
|
316
|
<title>Request Handler</title>
|
317
|
|
318
|
<para>The request handler takes the important task to handle and respond
|
319
|
to a request. There exists exactly one request handler for each request
|
320
|
type. By default web and command line requests are supported, but more
|
321
|
specialized request handlers can be developed, too.</para>
|
322
|
|
323
|
<para>Before one of the request handlers comes to play, the framework
|
324
|
needs to determine which of them is the most suitable for the current
|
325
|
request. The request handler resolver asks all of the registered request
|
326
|
handlers to rate on a scale how well they can handle the current raw
|
327
|
request. The resolver then chooses the request handler with the most
|
328
|
points and passes over the control.</para>
|
329
|
|
330
|
<para>Custom request handlers for special purposes just need to
|
331
|
implement the
|
332
|
<interfacename>\TYPO3\FLOW3\MVC\RequestHandlerInterface</interfacename>.
|
333
|
All classes implementing that interface are automatically registered and
|
334
|
will be considered while resolving a suitable request handler.</para>
|
335
|
</section>
|
336
|
|
337
|
<section>
|
338
|
<title>Request Builder</title>
|
339
|
|
340
|
<para>When a request handler receives a raw request, it needs to build a
|
341
|
request object which can be passed to the dispatcher and later to the
|
342
|
controller. The request building delegated to a request builder which
|
343
|
can build the required request type (ie. web, CLI etc.).</para>
|
344
|
|
345
|
<para>The building process mainly consists of</para>
|
346
|
|
347
|
<procedure>
|
348
|
<step>
|
349
|
<para>create a new request object</para>
|
350
|
</step>
|
351
|
|
352
|
<step>
|
353
|
<para>set some request-type specific parameters (like the request
|
354
|
URI for a web request)</para>
|
355
|
</step>
|
356
|
|
357
|
<step>
|
358
|
<para>determine and set the responsible controller, action and
|
359
|
action arguments</para>
|
360
|
</step>
|
361
|
</procedure>
|
362
|
|
363
|
<para>Especially the last step is important and requires some more or
|
364
|
less complex routing in case of web requests.</para>
|
365
|
</section>
|
366
|
|
367
|
<section>
|
368
|
<title>Request Dispatcher</title>
|
369
|
|
370
|
<para>The final task of the MVC framework consists in dispatching the
|
371
|
request to the controller specified in the request object. Dispatching
|
372
|
means that the request and response object is passed to the controller
|
373
|
specified in the request object and after the controller did its job,
|
374
|
control is returned to the request handler which eventually sends the
|
375
|
response.</para>
|
376
|
|
377
|
<para>The dispatch method itself is a loop which tries to invoke a
|
378
|
controller until a flag in the request object indicates that the request
|
379
|
has been dispatched. In most cases this loop has only one cycle: the
|
380
|
action method specified in the request is called and the action
|
381
|
controller automatically sets the <varname>dispatched</varname> flag
|
382
|
which leads to exiting the dispatch loop. However, when the controller
|
383
|
wants to forward or redirect the request to another controller or
|
384
|
action, the respective information is written to the request object and
|
385
|
the <varname>dispatched</varname> flag remains unset. Therefore the
|
386
|
dispatcher calls the next controller which hopefully can finally process
|
387
|
the request and exits the dispatch loop.</para>
|
388
|
|
389
|
<para>The dispatcher comes with a safeguard which assures that the
|
390
|
dispatching process does not end up in an endless loop.</para>
|
391
|
</section>
|
392
|
|
393
|
<section>
|
394
|
<title>Request Types</title>
|
395
|
|
396
|
<para>FLOW3 supports the most important request types out of the box.
|
397
|
Additional request types can easily be implemented by implementing the
|
398
|
<interfacename>\TYPO3\FLOW3\MVC\RequestInterface</interfacename> or
|
399
|
extending the <classname>\TYPO3\FLOW3\MVC\Request</classname> class and
|
400
|
registering a request handling which can handle the new request type
|
401
|
(and takes care of building the request object). Here are the request
|
402
|
types which come with the default FLOW3 distribution:</para>
|
403
|
|
404
|
<section>
|
405
|
<title>Web Request / Response</title>
|
406
|
|
407
|
<para>Web requests are the most common request types. Additional to
|
408
|
the common request functionality, this request type delivers
|
409
|
information about the request method, request URI and the base
|
410
|
URI.</para>
|
411
|
</section>
|
412
|
|
413
|
<section>
|
414
|
<title>CLI Request / Response</title>
|
415
|
|
416
|
<para>Requests from the command line are recognized by the used SAPI
|
417
|
(Server Application Programming Interface).</para>
|
418
|
</section>
|
419
|
</section>
|
420
|
</section>
|
421
|
|
422
|
<section xml:id="flow3.mvcframework.controller">
|
423
|
<title>Controller</title>
|
424
|
|
425
|
<para>The main responsibility of a controller is to process a request and
|
426
|
deliver a meaningful response. The
|
427
|
<interfacename>\TYPO3\FLOW3\MVC\Controller\ControllerInterface</interfacename>
|
428
|
therefore only contains two methods:</para>
|
429
|
|
430
|
<programlisting language="php">/**
|
431
|
* Checks if the current request type is supported by the controller.
|
432
|
*
|
433
|
* @param \TYPO3\FLOW3\MVC\RequestInterface $request The current request
|
434
|
* @return boolean TRUE if this request type is supported, otherwise FALSE
|
435
|
*/
|
436
|
public function canProcessRequest(\TYPO3\FLOW3\MVC\RequestInterface $request);
|
437
|
|
438
|
/**
|
439
|
* Processes a general request. The result can be returned by altering the given response.
|
440
|
*
|
441
|
* @param \TYPO3\FLOW3\MVC\RequestInterface $request The request object
|
442
|
* @param \TYPO3\FLOW3\MVC\ResponseInterface $response The response, modified by the controller
|
443
|
* @return void
|
444
|
* @throws \TYPO3\FLOW3\MVC\Exception\UnsupportedRequestTypeException if the controller doesn't support the current request type
|
445
|
*/
|
446
|
public function processRequest(\TYPO3\FLOW3\MVC\RequestInterface $request, \TYPO3\FLOW3\MVC\ResponseInterface $response);
|
447
|
</programlisting>
|
448
|
|
449
|
<para>However, only few applications will implement the whole request
|
450
|
processing logic themselves. Most of the time you'll be extending the
|
451
|
Action Controller.</para>
|
452
|
|
453
|
<section>
|
454
|
<title>Action Controller</title>
|
455
|
|
456
|
<para>The
|
457
|
<classname>\TYPO3\FLOW3\MVC\Controller\ActionController</classname>
|
458
|
processes a request by calling action methods. Actions are the
|
459
|
foundation of your application's workflow and logic. Which action is
|
460
|
called is usually determined by the URI as you have already seen in the
|
461
|
short hello world example.</para>
|
462
|
|
463
|
<section>
|
464
|
<title>Initialization Methods</title>
|
465
|
|
466
|
<para>Before an action method is called, the view, validation and
|
467
|
arguments are initialized. The following methods can be extended or
|
468
|
overloaded to hook into this initialization process:</para>
|
469
|
|
470
|
<itemizedlist>
|
471
|
<listitem>
|
472
|
<para><methodname>initializeView()</methodname> is called in order
|
473
|
to resolve, set and initialize a view which matches the current
|
474
|
action.</para>
|
475
|
</listitem>
|
476
|
|
477
|
<listitem>
|
478
|
<para><methodname>initializeAction()</methodname> is called after
|
479
|
the view has been initialized and the automatic registration of
|
480
|
arguments is done. At that point, the argument's values are still
|
481
|
empty and have not been validated.</para>
|
482
|
</listitem>
|
483
|
|
484
|
<listitem>
|
485
|
<para><methodname>initializeFooAction()</methodname> where
|
486
|
<methodname>Foo</methodname> is the name of the action, is called
|
487
|
only before an action of that name is called.</para>
|
488
|
</listitem>
|
489
|
</itemizedlist>
|
490
|
|
491
|
<para>Action arguments are usually registered automatically (see
|
492
|
below). If you wish to register additional arguments manually, you may
|
493
|
do that in one of the <methodname>initialize*Action()</methodname>
|
494
|
methods.</para>
|
495
|
</section>
|
496
|
|
497
|
<section>
|
498
|
<title>Configuration</title>
|
499
|
|
500
|
<para>The settings of the package containing your controller are
|
501
|
automatically injected into the action controller and can be accessed
|
502
|
through the <varname>$this->settings</varname> variable. Please
|
503
|
note that this variable contains all settings of the package as an
|
504
|
array, not only settings specific to your controller.</para>
|
505
|
|
506
|
<para>You should not modify the settings array because any information
|
507
|
you'd add would be lost anyway and is invisible to other parts of your
|
508
|
application.</para>
|
509
|
</section>
|
510
|
|
511
|
<section>
|
512
|
<title>Supported Request Types</title>
|
513
|
|
514
|
<para>The action controller generally supports any kind of request,
|
515
|
which means that theoretically you need only one controller for web
|
516
|
and CLI requests. In practice you might want to structure your
|
517
|
application so that a controller is responsable for only one request
|
518
|
type. If that is the case, you can define the supported request types
|
519
|
by setting the <property>supportedRequestTypes</property> property of
|
520
|
your class:</para>
|
521
|
|
522
|
<example>
|
523
|
<title>Defining the supported request types</title>
|
524
|
|
525
|
<programlisting language="php">/**
|
526
|
* My web-specific controller
|
527
|
*/
|
528
|
class StandardController extends \TYPO3\FLOW3\MVC\Controller\ActionController {
|
529
|
|
530
|
/**
|
531
|
* @var array
|
532
|
*/
|
533
|
protected $supportedRequestTypes = array('TYPO3\FLOW3\MVC\Web\Request');
|
534
|
|
535
|
</programlisting>
|
536
|
</example>
|
537
|
</section>
|
538
|
|
539
|
<section>
|
540
|
<title>Arguments</title>
|
541
|
|
542
|
<para>An action usually needs some more information about what it's
|
543
|
supposed to do. This information comes in form of GET or POST
|
544
|
arguments in case of a web request or via command line options if
|
545
|
we're dealing with a CLI request. Because there are even more ways to
|
546
|
pass information to an action and because this information needs
|
547
|
special care to assure a secure application, these arguments are
|
548
|
abstracted by FLOW3 in form of controller arguments.</para>
|
549
|
|
550
|
<para>The basic rule in FLOW3 is: an action method only gets those
|
551
|
arguments it asked for which have always values which are allowed for
|
552
|
the declared data type. Therefore arguments need to be registered and
|
553
|
it is not possible to access PHP's superglobals
|
554
|
<varname>$_GET</varname> and <varname>$_POST</varname>
|
555
|
directly.</para>
|
556
|
|
557
|
<para>Fortunately argument registration is very easy in FLOW3. It's
|
558
|
just a matter declaring the arguments in your action method
|
559
|
signature:</para>
|
560
|
|
561
|
<example>
|
562
|
<title>Declaring arguments in an action method</title>
|
563
|
|
564
|
<programlisting language="php">/**
|
565
|
* An example action method
|
566
|
*
|
567
|
* @param string $emailAddress Some email address
|
568
|
* @param string $streetName Some street name
|
569
|
* @param \TYPO3\Foo\Model\Customer $customer A customer object
|
570
|
* @return void
|
571
|
*/
|
572
|
public function exampleAction($emailAddress, $streetName, \TYPO3\Foo\Model\Customer $customer) {
|
573
|
|
574
|
}
|
575
|
</programlisting>
|
576
|
</example>
|
577
|
|
578
|
<para>FLOW3 will automatically register the arguments
|
579
|
<parameter>$emailAddress</parameter>,
|
580
|
<parameter>$streetName</parameter> and
|
581
|
<parameter>$customer</parameter>. Their expected data types are
|
582
|
<classname>Text</classname>, <classname>Text</classname> and
|
583
|
<classname>\TYPO3\Foo\customer</classname> respectively.</para>
|
584
|
|
585
|
<para>In essence this means that if you send a GET parameter
|
586
|
<parameter>emailAddress</parameter> with your request, it will end up
|
587
|
in <parameter>$emailAddress</parameter> if it is a valid
|
588
|
<classname>Text</classname> (which means: no HTML, no JavaScript, no
|
589
|
danger). Even the <parameter>$customer</parameter> argument will
|
590
|
contain a real <classname>Customer</classname> object if enough
|
591
|
information has been sent with the request.</para>
|
592
|
</section>
|
593
|
|
594
|
<section>
|
595
|
<title>Argument Validation</title>
|
596
|
|
597
|
<para>All arguments passed to the action methods will automatically
|
598
|
validated through their base validation rules defined in the
|
599
|
respective models. Additional validation rules can be defined by using
|
600
|
the <varname>@validate</varname> annotation. The syntax is similar to
|
601
|
a regular <varname>@validate</varname> declaration with the addition
|
602
|
that the argument name must be specified:</para>
|
603
|
|
604
|
<example>
|
605
|
<title>Additional validation rules in an action method</title>
|
606
|
|
607
|
<programlisting language="php">/**
|
608
|
* An example action method
|
609
|
*
|
610
|
* @param string $emailAddress Some email address
|
611
|
* @param string $streetName Some street name
|
612
|
* @param \TYPO3\Foo\Model\Customer $customer A customer object
|
613
|
* @return void
|
614
|
* @validate $emailAddress EmailAddress
|
615
|
* @validate $streeName Alphanumeric, StringLength(minimum = 5, maximum = 100)
|
616
|
* @validate $customer TYPO3\Foo\Validator\PlatinumCustomerValidator
|
617
|
*/
|
618
|
public function exampleAction($emailAddress, $streetName, \TYPO3\Foo\Model\Customer $customer) {
|
619
|
|
620
|
}
|
621
|
</programlisting>
|
622
|
</example>
|
623
|
|
624
|
<para>In a few situations it is necessary to disable validation for a
|
625
|
single argument, for example if an object needs to passed between
|
626
|
actions while it is beeing edited and therefore is knowingly in an
|
627
|
incomplete state. In these cases validation can be swtiched off
|
628
|
through the <literal>@dontvalidate</literal> annotation:</para>
|
629
|
|
630
|
<programlisting language="php">/**
|
631
|
* An example edit action method
|
632
|
*
|
633
|
* @param \TYPO3\Foo\Model\Customer $customer A customer object
|
634
|
* @return void
|
635
|
* @dontvalidate $customer
|
636
|
*/
|
637
|
public function editAction(\TYPO3\Foo\Model\Customer $customer) {
|
638
|
|
639
|
}</programlisting>
|
640
|
</section>
|
641
|
|
642
|
<section>
|
643
|
<title>Action Methods</title>
|
644
|
|
645
|
<para>$this->indexActionMethodName</para>
|
646
|
|
647
|
<para/>
|
648
|
</section>
|
649
|
</section>
|
650
|
|
651
|
<section>
|
652
|
<title>Other Controllers</title>
|
653
|
|
654
|
<section>
|
655
|
<title>Abstract Controller</title>
|
656
|
|
657
|
<para/>
|
658
|
</section>
|
659
|
|
660
|
<section>
|
661
|
<title>Request Handling Controller</title>
|
662
|
|
663
|
<para/>
|
664
|
</section>
|
665
|
|
666
|
<section>
|
667
|
<title>Standard Controller</title>
|
668
|
|
669
|
<para/>
|
670
|
</section>
|
671
|
|
672
|
<section>
|
673
|
<title>Not Found Controller</title>
|
674
|
|
675
|
<para>The
|
676
|
<classname>TYPO3\FLOW3\MVC\Controller\NotFoundController</classname> is
|
677
|
used whenever no other controller could be resolved which would match
|
678
|
the current request. It displays a generic "404 Page Not Found"
|
679
|
message.</para>
|
680
|
|
681
|
<para>It is possible to define your own custom controller which is
|
682
|
used in these cases. Just specify the object name in the FLOW3
|
683
|
settings.</para>
|
684
|
</section>
|
685
|
</section>
|
686
|
</section>
|
687
|
|
688
|
<section xml:id="flow3.mvcframework.view">
|
689
|
<title>View</title>
|
690
|
|
691
|
<para/>
|
692
|
|
693
|
<section>
|
694
|
<title>Template View</title>
|
695
|
|
696
|
<para/>
|
697
|
</section>
|
698
|
|
699
|
<section>
|
700
|
<title>Special Views</title>
|
701
|
|
702
|
<section>
|
703
|
<title>Standard View</title>
|
704
|
|
705
|
<para/>
|
706
|
</section>
|
707
|
|
708
|
<section>
|
709
|
<title>Empty View</title>
|
710
|
|
711
|
<para/>
|
712
|
</section>
|
713
|
</section>
|
714
|
</section>
|
715
|
|
716
|
<section xml:id="flow3.mvcframework.helpers">
|
717
|
<title>Helpers</title>
|
718
|
|
719
|
<para/>
|
720
|
</section>
|
721
|
|
722
|
<section xml:id="flow3.mvcframework.model">
|
723
|
<title>Model</title>
|
724
|
|
725
|
<para/>
|
726
|
</section>
|
727
|
|
728
|
<section xml:id="flow3.mvcframework.routing">
|
729
|
<title>Routing</title>
|
730
|
|
731
|
<para>As explained in the beginning of this chapter, in FLOW3 the
|
732
|
dispatcher passes the request to a controller which then calls the
|
733
|
respective action. But how to tell, what controller of what package is the
|
734
|
right one for the current request? This is were the routing framework
|
735
|
comes into play.</para>
|
736
|
|
737
|
<section>
|
738
|
<title>Router</title>
|
739
|
|
740
|
<para>The request builder asks the router for the correct package,
|
741
|
controller and action. For this it passes the current request path to
|
742
|
the routers <methodname>match</methodname> method. The router then
|
743
|
iterates through all configured routes and invokes their
|
744
|
<methodname>matches</methodname> method. The first route that matches,
|
745
|
determines which action will be called with what parameters.</para>
|
746
|
|
747
|
<para>The same works for the opposite direction: If a link is generated
|
748
|
the router calls the <methodname>resolve</methodname> method of all
|
749
|
routes until one route can return the correct URI for the specified
|
750
|
arguments.</para>
|
751
|
|
752
|
<para><note>
|
753
|
<para>If no matching route can be found, the
|
754
|
<methodname>indexAction</methodname> of the
|
755
|
<classname>StandardController</classname> of the
|
756
|
<package>FLOW3</package> package is called.</para>
|
757
|
</note></para>
|
758
|
</section>
|
759
|
|
760
|
<section>
|
761
|
<title>Route</title>
|
762
|
|
763
|
<para>A route describes the way from your browser to the controller -
|
764
|
and back.</para>
|
765
|
|
766
|
<para>With the <emphasis>URI pattern</emphasis> you can define how a
|
767
|
route is represented in the browsers address bar. By setting
|
768
|
<emphasis>defaults</emphasis> you can specify package, controller and
|
769
|
action that should apply when a request matches the route. Besides you
|
770
|
can set arbitrary default values that will be available in your
|
771
|
controller. They are called <emphasis>defaults</emphasis> because you
|
772
|
can overwrite them by so called <emphasis>dynamic route
|
773
|
parts</emphasis>.</para>
|
774
|
|
775
|
<para>But let's start with an easy example:<example>
|
776
|
<title>Simple route - Routes.yaml</title>
|
777
|
|
778
|
<programlisting language="yaml">--
|
779
|
name: 'Homepage'
|
780
|
uriPattern: ''
|
781
|
defaults:
|
782
|
'@package': Demo</programlisting>
|
783
|
</example><note>
|
784
|
<para>name is optional, but it's recommended to set a name for all
|
785
|
routes to make debugging easier.</para>
|
786
|
</note></para>
|
787
|
|
788
|
<para>If you insert these lines at the beginning of the file
|
789
|
<filename>Configurations/Routes.yaml</filename>, the
|
790
|
<methodname>indexAction</methodname> of the
|
791
|
<classname>StandardController</classname> in your
|
792
|
<package>Demo</package> package will be called when you open up the
|
793
|
homepage of your FLOW3 installation
|
794
|
(<uri>http://localhost/).</uri></para>
|
795
|
|
796
|
<para><note>
|
797
|
<para>You don't have to specify action and controller in this
|
798
|
example as the <methodname>indexAction</methodname> of the
|
799
|
<classname>StandardController</classname> is always called by
|
800
|
default.</para>
|
801
|
</note></para>
|
802
|
|
803
|
<section>
|
804
|
<title>URI pattern</title>
|
805
|
|
806
|
<para>The URI pattern defines the appearance of the URI. In a simple
|
807
|
setup the pattern only consists of <emphasis>static route
|
808
|
parts</emphasis> and is equal to the actual URI (without protocol and
|
809
|
host).</para>
|
810
|
|
811
|
<para>In order to reduce the amount of routes that have to be created,
|
812
|
you are allowed to insert markers, so called <emphasis>dynamic route
|
813
|
parts</emphasis>, that will be repaced by the routing framework. You
|
814
|
can even mark route parts <emphasis>optional</emphasis>.</para>
|
815
|
|
816
|
<para>But first things first.</para>
|
817
|
|
818
|
<section>
|
819
|
<title>Static route parts</title>
|
820
|
|
821
|
<para>A static route part is really simple - it will be mapped
|
822
|
one-to-one to the resulting URI without transformation.</para>
|
823
|
|
824
|
<para>Let's create a route that calls the listAction of the
|
825
|
CustomerController when browsing to
|
826
|
<uri>http://localhost/my/demo</uri>:<example>
|
827
|
<title>Simple route with static route parts -
|
828
|
Configuration/Routes.yaml</title>
|
829
|
|
830
|
<para><programlisting language="yaml">--
|
831
|
name: 'Static demo route'
|
832
|
uriPattern: 'my/demo'
|
833
|
defaults:
|
834
|
'@package': Demo
|
835
|
'@controller': Customer
|
836
|
'@action': list</programlisting></para>
|
837
|
</example></para>
|
838
|
</section>
|
839
|
|
840
|
<section>
|
841
|
<title>Dynamic route parts</title>
|
842
|
|
843
|
<para>Dynamic route parts are enclosed in curly brackets and define
|
844
|
parts of the URI that are not fixed.</para>
|
845
|
|
846
|
<para>Let's add some dynamics to the previous example:<example>
|
847
|
<title>Simple route with static and dynamic route parts -
|
848
|
Configuration/Routes.yaml</title>
|
849
|
|
850
|
<para><programlisting language="yaml">--
|
851
|
name: 'Dynamic demo route'
|
852
|
uriPattern: 'my/demo/{@action}'
|
853
|
defaults:
|
854
|
'@package': Demo
|
855
|
'@controller': Customer</programlisting></para>
|
856
|
</example>Now <uri>http://localhost/my/demo/list</uri> calls the
|
857
|
<methodname>listAction</methodname> just like in the previous
|
858
|
example.</para>
|
859
|
|
860
|
<para>With <uri>http://localhost/my/demo/index</uri> you'd invoke
|
861
|
the <methodname>indexAction</methodname> and so on.</para>
|
862
|
|
863
|
<para><note>
|
864
|
<para>It's not allowed to have successive dynamic route parts in
|
865
|
the URI pattern because it wouldn't be possible to determine the
|
866
|
end of the first dynamic route part then.</para>
|
867
|
</note></para>
|
868
|
|
869
|
<para>The @-prefix should reveal that action has a special meaning
|
870
|
here. Other predefined keys are @package, @subpackage, @controller
|
871
|
and @format. But you can use dynamic route parts to set any kind of
|
872
|
arguments:<example>
|
873
|
<title>dynamic parameters - Configuration/Routes.yaml</title>
|
874
|
|
875
|
<programlisting language="yaml">--
|
876
|
name: 'Dynamic demo route'
|
877
|
uriPattern: 'clients/{sortOrder}.{@format}'
|
878
|
defaults:
|
879
|
'@package': Demo
|
880
|
'@controller': Customer
|
881
|
'@action': list</programlisting>
|
882
|
</example></para>
|
883
|
|
884
|
<para>Browsing to <uri>http://localhost/clients/descending.xml</uri>
|
885
|
would call the <methodname>listAction</methodname> in your
|
886
|
<classname>Customer</classname> controller and the request argument
|
887
|
"sortOrder" had the value of "descending".</para>
|
888
|
|
889
|
<para>By default, dynamic route parts match anything apart from
|
890
|
empty strings. If you have more specialized requirements you can
|
891
|
create your custom route part handlers.</para>
|
892
|
</section>
|
893
|
|
894
|
<section>
|
895
|
<title>Route part handler</title>
|
896
|
|
897
|
<para>Route part handlers are classes that implement
|
898
|
<interfacename>TYPO3\FLOW3\MVC\Web\Routing\DynamicRoutePartInterface</interfacename>.
|
899
|
But for most cases it will be sufficient to extend
|
900
|
<classname>TYPO3\FLOW3\MVC\Web\Routing\DynamicRoutePart</classname> and
|
901
|
overwrite the methods <methodname>matchValue</methodname> and
|
902
|
<methodname>resolveValue</methodname>.</para>
|
903
|
|
904
|
<para>Let's have a look at the (very simple) route part handler of
|
905
|
the blog example:<example>
|
906
|
<title>BlogRoutePartHandler.php</title>
|
907
|
|
908
|
<programlisting language="php">class BlogRoutePartHandler extends \TYPO3\FLOW3\MVC\Web\Routing\DynamicRoutePart {
|
909
|
|
910
|
/**
|
911
|
* While matching, converts the blog title into an identifer array
|
912
|
*
|
913
|
* @param string $value value to match, the blog title
|
914
|
* @return boolean TRUE if value could be matched successfully, otherwise FALSE.
|
915
|
*/
|
916
|
protected function matchValue($value) {
|
917
|
if ($value === NULL || $value === '') return FALSE;
|
918
|
$this->value = array('__identity' => array('name' => $value));
|
919
|
return TRUE;
|
920
|
}
|
921
|
|
922
|
/**
|
923
|
* Resolves the name of the blog
|
924
|
*
|
925
|
* @param \TYPO3\Blog\Domain\Model\Blog $value The Blog object
|
926
|
* @return boolean TRUE if the name of the blog could be resolved and stored in $this->value, otherwise FALSE.
|
927
|
*/
|
928
|
protected function resolveValue($value) {
|
929
|
if (!$value instanceof \TYPO3\Blog\Domain\Model\Blog) return FALSE;
|
930
|
$this->value = $value->getName();
|
931
|
return TRUE;
|
932
|
}
|
933
|
}</programlisting>
|
934
|
</example></para>
|
935
|
|
936
|
<para>The corresponding route might look like this:<example>
|
937
|
<title>Route with route part handlers -
|
938
|
Configuration/Routes.yaml</title>
|
939
|
|
940
|
<programlisting language="yaml">--
|
941
|
name: 'Blog route'
|
942
|
uriPattern: 'blogs/{blog}/{@action}'
|
943
|
defaults:
|
944
|
'@package': Blog
|
945
|
'@controller': Blog
|
946
|
routeParts:
|
947
|
blog:
|
948
|
handler: TYPO3\Blog\RoutePartHandlers\BlogRoutePartHandler</programlisting>
|
949
|
</example></para>
|
950
|
|
951
|
<para>Have a look at the blog example for a working setup.</para>
|
952
|
</section>
|
953
|
|
954
|
<section>
|
955
|
<title>Optional route parts</title>
|
956
|
|
957
|
<para>By putting one or more route parts in round brackets you mark
|
958
|
them optional. The following route matches
|
959
|
<uri>http://localhost/my/demo/ </uri>and
|
960
|
<uri>http://localhost/my/demo/list.html</uri>.</para>
|
961
|
|
962
|
<example>
|
963
|
<title>Route with optional route parts -
|
964
|
Configuration/Routes.yaml</title>
|
965
|
|
966
|
<para><programlisting language="yaml">--
|
967
|
name: 'Dynamic demo route'
|
968
|
uriPattern: 'my/demo(/{@action}.html)'
|
969
|
defaults:
|
970
|
'@package': 'Demo'
|
971
|
'@controller': 'Customer'
|
972
|
'@action': 'list'</programlisting></para>
|
973
|
</example>
|
974
|
|
975
|
<note>
|
976
|
<para><uri>http://localhost/my/demo/list</uri> won't match here,
|
977
|
because either all optional parts have to match - or none.</para>
|
978
|
</note>
|
979
|
|
980
|
<note>
|
981
|
<para>You have to define default values for all optional dynamic
|
982
|
route parts.</para>
|
983
|
</note>
|
984
|
</section>
|
985
|
|
986
|
<section>
|
987
|
<title>Case sensitivity</title>
|
988
|
|
989
|
<para>By Default the case is not changed when creating URIs. The
|
990
|
following example with a username of "Kasper" will result in
|
991
|
<uri>http://localhost/Users/Kasper</uri><example>
|
992
|
<title>Route with default case handling</title>
|
993
|
|
994
|
<para><programlisting language="yaml">--
|
995
|
uriPattern: 'Users/{username}'
|
996
|
defaults:
|
997
|
@package: 'Demo'
|
998
|
@controller: 'Customer'
|
999
|
@action: 'show'</programlisting></para>
|
1000
|
</example></para>
|
1001
|
|
1002
|
<para>You can change this behavior for routes and/or dynamic route
|
1003
|
parts:<example>
|
1004
|
<title>Route with customised case handling</title>
|
1005
|
|
1006
|
<para><programlisting language="yaml">--
|
1007
|
uriPattern: 'Users/{username}'
|
1008
|
defaults:
|
1009
|
@package: 'Demo'
|
1010
|
@controller: 'Customer'
|
1011
|
@action: 'show'
|
1012
|
toLowerCase: true
|
1013
|
routeParts:
|
1014
|
username:
|
1015
|
toLowerCase: false</programlisting></para>
|
1016
|
</example>This will change the default behavior for this route and
|
1017
|
reset it for the username route part. Given the same username of
|
1018
|
"Kasper" the resulting URI will now be
|
1019
|
<uri>http://localhost/users/Kasper</uri> (note the lower case "u" in
|
1020
|
"users").</para>
|
1021
|
|
1022
|
<note>
|
1023
|
<para>The predefined route parts @package, @subpackage,
|
1024
|
@controller, @action and @format are an exception, they're always
|
1025
|
lower cased!</para>
|
1026
|
</note>
|
1027
|
|
1028
|
<para>Matching of incoming URIs is always done case insensitive. So
|
1029
|
both "Users/Kasper" and "users/Kasper" will match, and the value of
|
1030
|
the dynamic part will never be changed. If you want to handle data
|
1031
|
coming in through dynamic route parts case-insensitive, you need to
|
1032
|
handle that in your own code.</para>
|
1033
|
</section>
|
1034
|
</section>
|
1035
|
</section>
|
1036
|
|
1037
|
<section>
|
1038
|
<title>Subroutes</title>
|
1039
|
|
1040
|
<para>For security reasons and to avoid confusion, only routes
|
1041
|
configured in your global configuration folder are active. But FLOW3
|
1042
|
supports what we call subroutes enabling you to provide custom routes
|
1043
|
with your package and reference them in the global routing setup.</para>
|
1044
|
|
1045
|
<para>Imagine following routes in the <filename>Routes.yaml</filename>
|
1046
|
file inside your demo package:<example>
|
1047
|
<title>Demo Subroutes - Demo/Configuration/Routes.yaml</title>
|
1048
|
|
1049
|
<programlisting language="yaml">--
|
1050
|
name: 'Customer routes'
|
1051
|
uriPattern: '/clients/{@action}'
|
1052
|
defaults:
|
1053
|
'@controller': Customer
|
1054
|
|
1055
|
--
|
1056
|
name: 'Standard routes'
|
1057
|
uriPattern: '/{@action}'
|
1058
|
defaults:
|
1059
|
'@controller': Standard
|
1060
|
|
1061
|
--
|
1062
|
name: 'Fallback'
|
1063
|
uriPattern: ''
|
1064
|
defaults:
|
1065
|
'@controller': Standard
|
1066
|
'@action': index</programlisting>
|
1067
|
</example></para>
|
1068
|
|
1069
|
<para>And in your global <filename>Routes.yaml</filename>:<example>
|
1070
|
<title>Referencing subroutes - Configuration/Routes.yaml</title>
|
1071
|
|
1072
|
<programlisting language="yaml">--
|
1073
|
name: 'Demo subroutes'
|
1074
|
uriPattern: 'demo<DemoSubroutes>(.{@format})'
|
1075
|
defaults:
|
1076
|
'@package': Demo
|
1077
|
'@format': html
|
1078
|
subRoutes:
|
1079
|
DemoSubroutes:
|
1080
|
package: Demo</programlisting>
|
1081
|
</example></para>
|
1082
|
|
1083
|
<para>As you can see, you can reference subroutes by putting parts of
|
1084
|
the URI pattern in angle brackets (like <subRoutes>). With the
|
1085
|
subRoutes setting you specify where to load the subroutes from.</para>
|
1086
|
|
1087
|
<para>Internally the ConfigurationManager merges toghether the main
|
1088
|
route with its subroutes:<example>
|
1089
|
<title>Composite routes</title>
|
1090
|
|
1091
|
<programlisting language="yaml">--
|
1092
|
name: 'Demo subroutes :: Customer routes'
|
1093
|
uriPattern: 'demo/clients/{@action}(.{@format})'
|
1094
|
defaults:
|
1095
|
'@package': Demo
|
1096
|
'@format': html
|
1097
|
'@controller': Customer
|
1098
|
|
1099
|
--
|
1100
|
name: 'Demo subroutes :: Standard routes'
|
1101
|
uriPattern: 'demo/{@action}(.{@format})'
|
1102
|
defaults:
|
1103
|
'@package': Demo
|
1104
|
'@format': html
|
1105
|
'@controller': Standard
|
1106
|
|
1107
|
--
|
1108
|
name: 'Demo subroutes :: Fallback'
|
1109
|
uriPattern: 'demo(.{@format})'
|
1110
|
defaults:
|
1111
|
'@package': Demo
|
1112
|
'@format': html
|
1113
|
'@controller': Standard
|
1114
|
'@action': index</programlisting>
|
1115
|
|
1116
|
<para>You can even reference multiple subroutes from one route -
|
1117
|
that will create one route for all possible combinations.</para>
|
1118
|
</example></para>
|
1119
|
</section>
|
1120
|
</section>
|
1121
|
|
1122
|
<section xml:id="flow3.mvcframework.clirequests">
|
1123
|
<title>CLI request handling</title>
|
1124
|
|
1125
|
<para>FLOW3's CLI request handling offers a comfortable and flexible way
|
1126
|
of calling code from the command line:</para>
|
1127
|
|
1128
|
<para><command>php index.php [<replaceable>command</replaceable>]
|
1129
|
[<replaceable>options</replaceable>] [--]
|
1130
|
[<replaceable>arguments</replaceable>]</command></para>
|
1131
|
|
1132
|
<para><replaceable>command</replaceable>,
|
1133
|
<replaceable>options</replaceable> and
|
1134
|
<replaceable>arguments</replaceable> are optional, with varying results.
|
1135
|
The command structure follows what is commonly accpeted on unixoid systems
|
1136
|
for CLI programs:<variablelist>
|
1137
|
<varlistentry>
|
1138
|
<term>command</term>
|
1139
|
|
1140
|
<listitem>
|
1141
|
<para>If not given, the default controller of the FLOW3 package is
|
1142
|
used and it's index action is called. While this is an allowed
|
1143
|
call, it hardly makes sense (other than checking if FLOW basically
|
1144
|
works). If command is given then it is defined as
|
1145
|
<emphasis><replaceable>package</replaceable>
|
1146
|
[[<replaceable>sub1..N</replaceable>]
|
1147
|
<replaceable>controller</replaceable>
|
1148
|
<replaceable>action</replaceable>]</emphasis></para>
|
1149
|
|
1150
|
<para>First part is always the package. If only the package is
|
1151
|
given, it's StandardController's index action is called.</para>
|
1152
|
|
1153
|
<para>If at least three command parts are given, the last two
|
1154
|
sepcify controller and action. Anything in between specifys a sub
|
1155
|
package structure.<example>
|
1156
|
<title>Some FLOW3 CLI command specifications</title>
|
1157
|
|
1158
|
<para><literal>testing cli run</literal> would call the "run"
|
1159
|
action of the "cli" controller in the "Testing" package</para>
|
1160
|
|
1161
|
<para><literal>typo3cr admin setup foo</literal> would call
|
1162
|
the "setup" controller's "foo" action in the subpackage
|
1163
|
"admin" of the package "TYPO3CR"</para>
|
1164
|
</example></para>
|
1165
|
</listitem>
|
1166
|
</varlistentry>
|
1167
|
|
1168
|
<varlistentry>
|
1169
|
<term>options</term>
|
1170
|
|
1171
|
<listitem>
|
1172
|
<para>Options are either short- or long-style. The first option
|
1173
|
detected ends collecting command parts. Here are some
|
1174
|
examples:<example>
|
1175
|
<title>Giving options to FLOW3 CLI requests</title>
|
1176
|
|
1177
|
<para><literal>-o -f=value --a-long-option --with-spaces="is
|
1178
|
possible" --input file1 -o=file2 --event-this =
|
1179
|
works</literal></para>
|
1180
|
</example></para>
|
1181
|
</listitem>
|
1182
|
</varlistentry>
|
1183
|
|
1184
|
<varlistentry>
|
1185
|
<term>arguments</term>
|
1186
|
|
1187
|
<listitem>
|
1188
|
<para>Arguments can follow and will be available to the called
|
1189
|
controller in the request object. To distinguish between
|
1190
|
<replaceable>command</replaceable> and
|
1191
|
<replaceable>arguments</replaceable> in cases where no
|
1192
|
<replaceable>options</replaceable> are given the seperator
|
1193
|
<literal>--</literal> must be used.</para>
|
1194
|
</listitem>
|
1195
|
</varlistentry>
|
1196
|
</variablelist></para>
|
1197
|
</section>
|
1198
|
</chapter>
|