-
Notifications
You must be signed in to change notification settings - Fork 6
/
agavi-faq.html
executable file
·3038 lines (2677 loc) · 164 KB
/
agavi-faq.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Agavi common questions and answers (FAQ)</title>
<link rel="stylesheet" href="css/faq-default.css" type="text/css" media="screen">
<link type="text/css" rel="stylesheet" href="shjs-0.6/css/sh_nedit.css">
</head>
<body onload="sh_highlightDocument('shjs-0.6/lang/', '.js');" id="faq">
<div>
<h1>Frequently Asked Questions (FAQ) - Agavi PHP Framework</h1>
<p class="hint">I uploaded this document to github. You can find it under <a href="http://github.com/graste/Agavi-FAQ" title="Visit Agavi FAQ on GitHub...">http://github.com/graste/Agavi-FAQ</a>.
<p class="warning">
This <strong>unofficial</strong> FAQ is a work in progress. All information is provided as is without any guarantees. Use
it at your own risk. Oh, and beware of that yellow fade animated gif in the background. Instant eye cancer. ;-)<br>
</p>
<div class="introduction">
<p>
On this page a few common questions are answered. Examples are given where appropriate.
If you have questions that are not answered here, visit the user mailing list, join the <a href="http://trac.agavi.org/wiki/IRC" title="Go to IRC info page">IRC channel</a>
or search through <a href="http://lists.agavi.org/mailman/listinfo" title="Go to mailing list info page">mailing list archive</a>
or <a href="http://www.agavi.org/irclogs/" title="Go to IRC logs page">IRC channel logs</a> as a start.
The <a href="http://trac.agavi.org/browser/branches/1.0/src/" title="Go to trac SVN view of Agavi 1.0 sources...">Agavi source code</a>
is well documented and the <a href="http://trac.agavi.org/browser/branches/1.0/samples/" title="Go to trac SVN view of Agavi 1.0 sample application...">sample application</a>
should give you some good practices to start with. In case you want a question added here,
ask via mail (<a href="mailto:[email protected]" title="Send email to author">[email protected]</a>) or
visit the IRC channel (ask graste).</p>
<p>
The examples in this FAQ are skeletons most of the time. It is recommended to be strict when
checking user input such as header variables, cookies, files or request parameters. The validator
examples are just there to get you started with some features. The usage of fragment caching may
lead to side effects if you define caches only for production environment and then begin to wonder
why some code or slot or whatever doesn't get executed as it always was before. Please think twice
before copying and pasting example code (which is not production ready on itself most of the time).
</p>
<p>
Most <a href="http://www.satisfice.com/blog/archives/27" title="Go to 'No Best Practices' article by James Bach...">good practices or patterns</a>
are probably to be found in the <a href="http://trac.agavi.org/browser/branches/1.0/samples/" title="Go to trac SVN view of Agavi 1.0 sample application...">sample application</a>
or the official <a href="http://www.agavi.org/documentation/tutorial" title="Go to introductory Agavi quick start tutorial...">Agavi quick start tutorial</a>.
</p>
</div>
<p class="hint">
With Agavi you can have different contexts (think 'web', 'console' or 'soap') and multiple environments
(like 'development', 'staging', 'production' etc.). The <strong><code><ae:configuration></code> sections
in the config files are sorted first (vanilla, environment-only, context-only, environment+context) and
then merged together.</strong> Knowing this, you can just define default settings and then overwrite them
in context- or environment-specific sections. Some examples below make use of this. You may use regular expressions
like this in the attribute: <code>environment="development.*"</code>. This would allow for common settings for
all environments that start with <code>development</code> (so John and Chuck from your dev-team get those
settings in their private <code>development-john-rambo</code> or <code>development-chuck-norris</code>
configuration sections).
</p>
<h2 id="general-questions">General topics</h2>
<ol>
<li><a href="#general_1">Is there a way to change the action method mappings of HTTP verbs (e.g. swap PUT/POST for RESTful applications)?</a></li>
<li><a href="#general_2">Is there any way to set a template file extension per output type?</a></li>
<li><a href="#general_4">What's that <code>isSimple()</code> method all about (in an action)?</a></li>
<li><a href="#general_5">XInclude? RelaxNG? Schema? Namespaces? How can I make use of that?</a></li>
<li><a href="#general_6">How do I return XML to the client (like in an Atom/RSS newsfeed)?</a></li>
<li><a href="#general_7">How do I implement AJAX- or JSON-related solutions using Agavi?</a></li>
<li><a href="#general_8">How to set different exception templates (e.g. for AJAX requests)?</a></li>
<li><a href="#general_9">Where to store application settings and how to access them?</a></li>
<li><a href="#general_10">How to set custom database connection properties (e.g. for Propel or Doctrine)?</a></li>
<li><a href="#general_11">How to get <code>HTTP_*</code> variables?</a></li>
<li><a href="#general_12">What about cookies and how can I get/set/remove them?</a></li>
<li><a href="#general_13">How to do forwards and redirects?</a></li>
<li><a href="#general_14">How to return HTTP status codes?</a></li>
<li><a href="#general_15">Where can I change the session settings?</a></li>
<li><a href="#general_16">Fast! I need an example of slots and how to use them!</a></li>
<li><a href="#general_17">How to load different layouts?</a></li>
<li><a href="#general_18">How to re-use input templates to display errors?</a></li>
<li><a href="#general_19">How to load another master template?</a></li>
<li><a href="#general_20">What is the Agavi startup sequence?</a></li>
<li><a href="#general_21">What do I need to know about Agavi models and how to use them?</a></li>
<li><a href="#general_22">How to use custom configuration files?</a></li>
<li><a href="#general_23">How to use Propel ORM models with Agavi?</a></li>
<li><a href="#general_24">How to change the template lookup patterns Agavi uses?</a></li>
<li><a href="#general_25">How to use different renderers for different layers of output types?</a></li>
<li><a href="#general_99">Is it possible to change the directory structure of a module?</a></li>
</ol>
<h2 id="routing-questions">Routing related topics</h2>
<ol>
<li><a href="#routing_0">What do I have to know about Agavi's routing system?</a></li>
<li><a href="#routing_1">How to define default values for routing parameters?</a></li>
<li><a href="#routing_2">How to use those curly braces for min/max quantifiers?</a></li>
<li><a href="#routing_3">How to make route pattern matching case-insensitive?</a></li>
<li><a href="#routing_4">What additional options are there to be specified when generating routes?</a></li>
<li><a href="#routing_5">How to determine the current matched routes?</a></li>
<li><a href="#routing_6">What are routing callbacks and how can I use them?</a></li>
<li><a href="#routing_7">How do I generate routes with different output types?</a></li>
</ol>
<h2 id="validation-questions">Validation related topics</h2>
<ol>
<li><a href="#validation_0">What are the validation modes and what do I need to know about them?</a></li>
<li><a href="#validation_1">Is it possible to define nested validators?</a></li>
<li><a href="#validation_2">What is the name of a validator for?</a></li>
<li><a href="#validation_3">How to prevent performing other validations on a field in case one validator fails?</a></li>
<li><a href="#validation_4">Can validators depend on other validators?</a></li>
<li><a href="#validation_5">Can the "depends" attribute receive more than one dependency?</a></li>
<li><a href="#validation_6">Is there a possibility to register validators manually in an action?</a></li>
<li><a href="#validation_7">Is there any example of custom file validations or the <code>AgaviImageFileValidator</code>?</a></li>
<li><a href="#validation_8">How can I repopulate readonly/disabled input fields?</a></li>
<li><a href="#validation_9">How to manually output error messages in a view?</a></li>
<li><a href="#validation_10">Is there a better way than using <code>isSet</code> or <code>isNotEmpty</code> to validate route parameters?</a></li>
<li><a href="#validation_11">How do I use the <code>inarray</code> validator?</a></li>
<li><a href="#validation_12">How do I validate array input?</a></li>
<li><a href="#validation_13">Is there an example for the <code>AgaviDateTimeValidator</code>?</a></li>
<li><a href="#validation_14">How do I check validation results and retrieve the incidents of a failed validator?</a></li>
<li><a href="#validation_15">How to handle checkbox input fields?</a></li>
<li><a href="#validation_16">I'm writing the same validation code over and over again, which doesn't feel very DRY. How to solve this?</a></li>
<li><a href="#validation_17">How to write a custom validator?</a></li>
<li><a href="#validation_18">How to register shortnames for validators and why should I use them?</a></li>
<!-- multiple forms on page: write is called on slots that contain forms and so validation fails -->
</ol>
<h2 id="caching-questions">Caching related topics</h2>
<ol>
<li><a href="#caching_0">Is it possible to (globally) enable or disable fragment caching?</a></li>
<li><a href="#caching_1">What are caching groups and how are they used for fragment caching?</a></li>
<li><a href="#caching_2">Which <code>source</code> attribute values are available and what do they do?</a></li>
<li><a href="#caching_3">Any tips before starting to use fragment caching intensively?</a></li>
</ol>
<h2 id="translation-questions">Translation related topics</h2>
<ol>
<li><a href="#translation_0">Quickstart</a></li>
<li><a href="#translation_1">How do I use and customize a currency format?</a></li>
</ol>
<h2 id="filter-questions">Filter related topics</h2>
<ol>
<li><a href="#filter_0">What kind of filters are available by default and how to configure them?</a></li>
<li><a href="#filter_1">tbd (FormPopulationFilter tipps)</a></li>
<li><a href="#filter_2">What's the execution order of filters?</a></li>
</ol>
<h2 id="testing-questions">Testing related topics</h2>
<ol>
<li><a href="#testing_0">How do I start?</a></li>
<!-- <li><a href="#testing_1">Which types of tests are supported by Agavi and where to put them?</a></li> -->
</ol>
<h2 id="misc-questions">Misc topics</h2>
<ol>
<li><a href="#misc_0">How do I get improved code completion in Netbeans or Eclipse or <code>$favoriteIDE</code>?</a></li>
<li><a href="#misc_1">How do I set an exit code in console context or shell applications?</a></li>
</ol>
<hr>
<h2 id="general-answers">General topics</h2>
<dl>
<dt id="general_1">Is there a way to change the action method mappings of HTTP verbs (e.g. swap PUT/POST for RESTful applications)?</dt>
<dd>By default Agavi maps <code>executeCreate()</code> with HTTP PUT, <code>executeWrite()</code>
with HTTP POST, <code>executeRead()</code> with HTTP GET and <code>executeRemove()</code> with
HTTP DELETE. If you want to change these mappings modify the <code>app/config/factories.xml</code>
file and add the following:
<pre class="sh_xml">
<request class="AgaviWebRequest">
<ae:parameter name="method_names">
<ae:parameter name="POST">create<ae:parameter>
<ae:parameter name="GET">read<ae:parameter>
<ae:parameter name="PUT">write<ae:parameter>
<ae:parameter name="DELETE">remove<ae:parameter>
<ae:parameter>
<request>
</pre>
The above mappings are the default ones defined by Agavi. Please note, that you may use any arbitrary request method you want to support.
<p class="hint">To support uncommon or custom request methods use a specific <code>execute[Requestmethod]()</code> in your
actions to react to different specific request methods OR use the <code>execute()</code> method for any request method. Let's say you
want your application to react to <code>OPTIONS</code> or <code>HEAD</code> requests: Just create <code>executeOptions()</code> and
<code>executeHead()</code> in your action and be done. Remember, that you can only get validated parameters, headers, files
and cookies. To test your <code>executeHead()/executeOptions()</code> you may use something like
<code>curl -I http://route/to/action</code> or <code>curl -X OPTIONS http://route/to/action</code>.</p>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_2">Is there any way to set a template file extension per output type?</dt>
<dd>There are multiple possibilities. You can set an extension per individual layer using
<code>$this->getLayer('content')->setExtension($whatever);</code> or do this in the config
or set the default_extension parameter of the renderer. You could set a
<code><parameter name="extension">.whatever</parameter></code> to the relevant
layer as <code>setExtension()</code> is just a <code>__call()</code> overload for <code>setParameter('.extension')</code>.
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_4">What's that <code>isSimple()</code> method all about (in an action)?</dt>
<dd>The <code>isSimple()</code> method determines whether the actions' <code>validate*</code>
and <code>execute*</code> methods are called. If <code>isSimple()</code> returns <code>true</code>,
action validation and execution are bypassed and the default view (see <code>getDefaultView()</code>)
is executed. Note also, that <em>no filters are run (not even the security filter)</em> and
no request data is available except for arguments explicitly passed to the container.<br><br>
If you want to use an action both normal and as a simple slot you can try this:
<pre class="sh_php">
public function isSimple()
{
return (bool)$this->getContainer()->getParameter('is_slot'); // only simple when run as slot
}
</pre>
Remember, that you have to validate parameters given to an action, if that action's not simple
(and you only get parameters if there's at least an <code>execute*()</code> method).
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_5">XInclude? RelaxNG? Schema? Namespaces? How can I make use of that?</dt>
<dd>TBD - for some examples see the sample application's <code>output_types.xml</code> file:
<pre class="sh_xml">
<!-- include common layer definitions from the sandbox element -->
<xi:include xpointer="xmlns(ae=http://agavi.org/agavi/config/global/envelope/1.0)
xmlns(ot=http://agavi.org/agavi/config/parts/output_types/1.0)
xpointer(/ae:configurations/ae:sandbox/ot:layers/*)" />
</pre>
<!--
<route name=".product" pattern="^/(id:\d+)" action="Product">
<route name=".view" pattern="^$" action=".View" />
< there are also route ".edit", ".delete" etc
Where should I place validator? Is it possible to create validators chain?
you can use parent="..." and xincludes to import shared validation definitions
in case you have nested routes like that etc
no, validation for routes, each level of routes has its own validation file. SOmething like that
in the example above I have to include the same code (that checks id) into all validation XMLs.
-->
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_6">How do I return XML to the client (like in an Atom/RSS newsfeed)?</dt>
<dd>".xml" route and xml output type with executeXml() method to return xml document instead of template
<a href="http://groups.google.com/group/agavi-users/browse_thread/thread/b476c38d1afe9b6f">use something like this as example...</a>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_7">How do I implement AJAX- or JSON-related solutions using Agavi?</dt>
<dd>similar to the answer above - define route and json output type and then executeJson() method returns json content
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_8">How to set different exception templates (e.g. for AJAX requests)?</dt>
<dd>You can set a different template in your output type definition like this:
<pre class="sh_xml">
<output_type exception_template="...">
</pre>
Default exception templates and custom ones per context may be set via <code>settings.xml</code> file. Customizations according
to the needs of your application are possible per environment, context and output type:
<pre class="sh_xml">
<!-- in settings.xml -->
<exception_templates>
<!--
this is the exception template that's used by default
unless one for a specific context has been defined, or
for the current output type
-->
<!--
note that exceptions that occur before a context is even
fully initialized, or an exception that happens before
the output types are loaded and determined, will use a
template defined here
-->
<exception_template>%core.agavi_dir%/exception/templates/shiny.php</exception_template>
<!-- an example for per-context exception templates -->
<!-- per-output-type templates can be set in output_types.xml -->
<exception_template context="console">%core.agavi_dir%/exception/templates/plaintext.php</exception_template>
</exception_templates>
<!-- in output_types.xml -->
<ae:configuration environment="production.*">
<output_types default="html">
<!-- use a different exception template in production environments for HTML -->
<output_type name="html" exception_template="%core.template_dir%/exceptions/web-html.php" />
</output_types>
</ae:configuration>
</pre>
This makes it possibles to have the verbose shiny exception template in development while using simple HTTP 500 status error pages in production.
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_9">Where to store application settings and how to access them?</dt>
<dd>Application wide settings can easily be stored in <code>app/config/settings.xml</code>.
You can define different settings and values for all environments and contexts you use.
<pre class="sh_xml">
<ae:configuration>
<settings prefix="Blog.">
<setting name="enabled">true</setting>
<setting name="EntriesPerPage">25</setting>
<setting name="Theme">green</setting>
<setting name="More">
<ae:parameters>
<ae:parameter name="foo">bar</ae:parameter>
<ae:parameter name="blah">blub</ae:parameter>
</ae:parameters>
</setting>
</settings>
</ae:configuration>
<ae:configuration environment="production">
<settings prefix="Blog.">
<setting name="EntriesPerPage">10</setting>
</settings>
</ae:configuration>
</pre>
You then get values like this:
<pre class="sh_php">
$entries_per_page = AgaviConfig::get('Blog.EntriesPerPage', $default_value_if_not_defined);
</pre>
Getting the <code>Blog.More</code> settings entry will result in an indexed array you can use.<br><br>
If you have settings that are specific to various modules you can use each module's
<code>module.xml</code> files to store values. You access these settings like this:
<pre class="sh_php">
AgaviConfig::get('modules.lowercase_module_name.SomeSetting');
</pre>
Remember, that you can always set values programatically using:
<pre class="sh_php">
AgaviConfig::set('some.setting', 'some_value');
</pre>
as you can see in you application's <code>index.php</code> or <code>config.php</code> files. Another thing to
know about the different settings in different contexts and environments is the
following: <strong><code><ae:configuration></code> sections are sorted first (vanilla, environment-only,
context-only, environment+context) and then merged together.</strong> Knowing this you can just define
default settings and then overwrite them in context- or environment-specific sections like
in the example above.
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_10">How to set custom database connection properties (e.g. for Propel or Doctrine)?</dt>
<dd>You can set additional options to your database connections in your <code>app/config/database.xml</code>
file (see that <code>settings</code> parameter):
<pre class="sh_xml">
<ae:configurations xmlns:ae="http://agavi.org/agavi/config/global/envelope/1.0"
xmlns="http://agavi.org/agavi/config/parts/databases/1.0">
<ae:configuration environment="development">
<databases default="db">
<database name="propel" class="AgaviPropelDatabase">
<ae:parameter name="config">%core.app_dir%/config/propelproject-conf.php</ae:parameter>
<ae:parameter name="overrides">
<ae:parameter name="connection">
<ae:parameter name="dsn">mysql:dbname=icanhazdb;host=127.0.0.1</ae:parameter>
<ae:parameter name="user">w00t</ae:parameter>
<ae:parameter name="password">not_default_pass</ae:parameter>
<ae:parameter name="settings">
<ae:parameter name="charset">
<ae:parameter name="value">utf8</ae:parameter>
</ae:parameter>
</ae:parameter>
</ae:parameter>
</ae:parameter>
</database>
<database name="db" class="AgaviDoctrineDatabase">
<ae:parameters>
<ae:parameter name="attributes">
<ae:parameters>
<ae:parameter name="Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES">true</ae:parameter>
<ae:parameter name="Doctrine::ATTR_USE_NATIVE_ENUM">true</ae:parameter>
<ae:parameter name="Doctrine_Core::ATTR_VALIDATE">LENGTHS</ae:parameter>
<ae:parameter name="Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE">true</ae:parameter>
<ae:parameter name="Doctrine_Core::ATTR_QUOTE_IDENTIFIER">true</ae:parameter>
</ae:parameters>
</ae:parameter>
<ae:parameter name="manager_attributes">
<ae:parameters>
<ae:parameter name="Doctrine_Core::ATTR_MODEL_LOADING">2</ae:parameter>
</ae:parameters>
</ae:parameter>
<ae:parameter name="load_models">%core.lib_dir%/orm</ae:parameter>
<ae:parameter name="charset">UTF8</ae:parameter>
</ae:parameters>
</database>
</databases>
</ae:configuration>
<!--
Simple example on how to include e.g. more sensitive database settings from an external file that may have been
generated on deployment or similar and contains only things like hostname, db name, port, username and password.
-->
<xi:include href="%core.app_dir%/../etc/local.databases.xml" xpointer="xmlns(ae=http://agavi.org/agavi/config/global/envelope/1.0) xpointer(/ae:configurations/*)">
</xi:include>
</ae:configurations>
</pre>
This example's taken from <a href="http://dracoblue.net/dev/utf8-connections-with-propel-in-agavi/127/" title="Go to DracoBlue's blog entry...">DracoBlue's blog entry</a> and extended by a bit of XInclude magic and Doctrine settings.
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_11">How to get <code>HTTP_*</code> variables?</dt>
<dd>The <code>AgaviWebRequestDataHolder</code> defines <code>headers</code>, <code>files</code>
and <code>cookies</code> as sources (apart from GET/POST parameters, which you simply access
by validating them and then using <code>$rd->getParameter('name', $default)</code>).<br>
The <code>headers</code> source contains all <code>HTTP_*</code> variables you might want to use. The
only thing between you and your values is strict validation, as these settings are
user provided request data and therefore unvalidated input. Here is an example how to access header variables:
<pre class="sh_php">
// note the stripped HTTP_ prefix and the UPPERCASE characters:
$rd->get('headers','USER_AGENT'); // AgaviRequestDataHolder class
$rd->getHeader('User-Agent'); // AgaviWebRequestDataHolder class
</pre>
You validate them by using the <code>source</code> parameter in your validator specification:
<pre class="sh_xml">
<validator ... source="headers">
<argument>USER_AGENT</argument>
</validator>
<-- or perhaps for referer checking: -->
<validator ... source="headers">
<-- you could e.g. check for a valid (and/or allowed) URL, the non-existance
of Javascript/HTML/SQL/XPath/whatever code snippets etc. -->
<argument>REFERER</argument>
</validator>
</pre>
<div class="warning">
We urge you to be _really_ strict when checking user input such as header variables, cookies, files or parameters.
It is _not_ proficient to just use a simple <code>string</code> validator as people could send you Javascript, SQL
or whatever malicious input data not only using parameters, but instead trying (sometimes delayed) attacks using cookies
and headers. The next time you use plain SQL and someone surfs on your page using a referer set to
<strong>' OR 1=1 -- "|#</strong> you will definitively be thankful for strict validation and _good_
validation rules in your validators.
</div>
<br>
<p class="hint">Please note, that you can access <code>$_SERVER</code> (like <code>$_SERVER['SERVER_NAME']</code>) and <code>$_ENV</code> directly,
as it is mostly not user provided data (and not really standardized as well).</p>
To give you a very simple example of a custom validator, that exports an <code>IS_AJAX</code> header parameter to your action if the
current request was issued via Javascript's <code>XmlHttpRequest</code> method, see the following snippets:
<pre class="sh_xml">
<!-- validate.xml -->
<validator class="SearchHttpXRequestedWithValidator" name="no_ajax_submit" required="false" provides="search[submit_search]" source="headers">
<arguments>
<argument>X_REQUESTED_WITH</argument>
</arguments>
<ae:parameters>
<ae:parameter name="export">IS_AJAX</ae:parameter>
</ae:parameters>
</validator>
</pre>
and
<pre class="sh_php">
/* SearchHttpXRequestedWithValidator.class.php */
class SearchHttpXRequestedWithValidator extends AgaviStringValidator
{
protected function validate()
{
$argument = $this->getArgument();
$value = $this->getData($argument);
$this->export(false !== stristr($value, 'XMLHttpRequest'));
return true;
}
protected function checkAllArgumentsSet($throwError = true)
{
return true; // see AgaviValidator::checkAllArgumentsSet(); used to export default value in case of missing parameter
}
}
</pre>
Just add the custom <code>SearchHttpXRequestedWithValidator</code> to an <code>autoload.xml</code>, use it in a validation XML file and ask
for AJAX requests in your action's <code>execute*()</code> method (or wherever) like this:
<pre class="sh_php">
$this->is_ajax_request = $rd->getHeader('IS_AJAX', false);
</pre>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_12">What about cookies and how can I get/set/remove them?</dt>
<dd>Example how to set a cookie in a view:
<pre class="sh_php">
$this->getResponse()->setCookie('cookieName', $value);
$this->getResponse()->setCookie('cookieName', $value, '+14 days'); // notice the hot strtotime syntax
</pre>
If you want to read cookies, you have to validate them as they are user provided input data. Other cookie related
methods can be found in <a href="http://trac.agavi.org/browser/branches/1.0/src/response/AgaviWebResponse.class.php#L465" title="Go to AgaviWebResponse class...">AgaviWebResponse</a>.
To remove a cookie just call <code>unsetCookie($name, $path, $domain, $secure, $httponly)</code> with the same
parameters you used for setting the cookie. To get a cookie value simply call <code>getCookie($name)</code>.
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_13">How to do forwards and redirects?</dt>
<dd>You can either forward internally to another action or do a full redirect to another absolute
URL (e.g. redirect-after-post pattern):
<pre class="sh_php">
public function executeHtml(AgaviRequestDataHolder $rd)
{
// ... some code ...
if (some_constraint)
{
// redirect to a specific URL:
$this->getContainer()->getResponse()->setRedirect($absolute_target_url);
// or redirect like this ('relative'=>false may be omitted as Agavi sets it automatically for you)
$this->getResponse()->setRedirect($this->getContext()->getRouting()->gen('route.to.somewhere', $params_array, array('relative' => false)));
return; // no false or AgaviView::NONE here
}
// or forward to another action (the default 404 page in this case):
return $this->createForwardContainer(AgaviConfig::get('actions.error_404_module'), AgaviConfig::get('actions.error_404_action'));
}
</pre>
When forwarding, the output type et cetera should be respected. If you want to set these things explicitly, use the three
optional parameters <code>$arguments</code>, <code>$outputType</code> and <code>$requestMethod</code>.
<div class="hint">Forwarding and redirecting only works inside views in Agavi. This is due to the
fact, that redirecting and forwarding works differently for each output type. E.g. HTTP redirects in HTML
based (web-) applications aren't available in SOAP or XML-RPC contexts. As your actions are not supposed
to know, which output type they support (now or in the future), you do all your redirects and forwards in views.
</div>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_14">How to return HTTP status codes?</dt>
<dd>Just do something like this in your view:
<pre class="sh_php">
$this->getResponse()->setHttpStatusCode('301'); // 301 moved permanently
$this->getResponse()->setHttpStatusCode('304'); // 304 not modified
$this->getResponse()->setHttpStatusCode('402'); // 402 payment required ;-)
$this->getResponse()->setHttpStatusCode('404'); // 404 not found
$this->getResponse()->setHttpStatusCode('405'); // 405 method not allowed
$this->getResponse()->setHttpStatusCode('500'); // 500 internal server error
// etc. pp., you probably know all those HTTP codes...
</pre>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_15">Where can I change the session settings?</dt>
<dd>By default Agavi uses the session settings of your PHP installation. The default
name of the Agavi session cookie is <code>Agavi</code>. You can override the PHP defaults
by adding parameters to the <code><storage></code> element in <code>app/config/factories.xml</code>.
<pre class="sh_xml">
<storage class="AgaviSessionStorage">
<ae:parameter name="session_save_path">some path</ae:parameter> <!-- current directory used for session data storage -->
<ae:parameter name="session_cookie_lifetime">10800</ae:parameter> <!-- lifetime of the session cookie in seconds -->
<ae:parameter name="session_cookie_path">/</ae:parameter> <!-- path on the domain (single slash for all paths) -->
<ae:parameter name="session_cookie_domain">www.example.com</ae:parameter> <!-- cookie domain (be careful with leading '.' as cookies
are then visible to all subdomains) -->
<ae:parameter name="session_cookie_secure">true</ae:parameter> <!-- true for secure connection cookie only -->
<ae:parameter name="session_cookie_httponly">true</ae:parameter> <!-- set httponly flag (beware of XMLHttpRequest issues
- otherwise it's not a bad measure (even though it doesn't protect you e. g. from HTTP TRACE attacks or the like) -->
<!-- for other parameters see AgaviSessionStorage file -->
</storage>
</pre>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_16">Fast! I need an example of slots and how to use them!</dt>
<dd>Slots are a decent solution to reuse existing functionality. You may either
define slots in <code>app/config/output_types.xml</code> (to include them in the
master template) or create/delete slot containers dynamically "on the fly" in your views.
Defining slots inside <code><layer></code> definitions in <code>output_types.xml</code>
could look like this:
<pre class="sh_xml">
<slots>
<slot name="header" module="Default" action="Header" />
<slot name="navigation" module="Default" action="Navigation.Main" />
<ae:parameter name="page">%core.template_dir%/static/page.html</ae:parameter>
</slot>
<slot name="footer" module="Default" action="Footer"/>
</slots>
</pre>
To be able to use the given parameters in a slot you have to validate them. If you want to
create slot containers in a view, try it like this:
<pre class="sh_php">
// in view:
$this->getLayer('content')->setSlot('slotName', $this->createSlotContainer(
'ModuleName', // name of module to use
'Some.AwesomeActionName', // name of action to execute
array('param' => 'value'), // parameters to pass to the slot
'html', // output type to use
'write' // request method to use
));
// in template:
if (!empty($slots['slotName'])
{
echo $slots['slotName'];
}
</pre>
You don't have to pass parameters explicitly to the slot, as it will have a copy of all the
current request parameters. The output type and request method parameters are optional, too.
To give you an impression on how flexible Agavi's output type, layer and slot handling is, see this:
<pre class="sh_php">
// to change the action of a specific slot (you can set the module name as well)
$this->getLayer('content')->getSlot('slotName')->setActionName('SomeNewActionName');
// create and append layer when you need one in a view
$this->appendLayer($this->createLayer('AgaviFileTemplateLayer', 'content'));
</pre>
<br>
<div class="hint">
Slots are rendered <em>after the main action/view</em> has run, but <em>before the main template</em> is rendered.
Decorators are rendered after that. This makes it possible to change the layout to be used in the
main view and add and remove slots dynamically as you like. Additionally you can set request
attributes for later use in the decorator slots.
</div>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_17">How to load different layouts?</dt>
<dd>You may define different layouts in your <code>app/config/output_types.xml</code> and
then choose which layout to load in your views like this:
<pre class="sh_php">
$this->setupHtml($rd, 'layoutName');
</pre>
Imagine you have some modules and each module has multiple (nested) actions. You then can easily
override <code>setupHtml</code> in each module's <code>BaseView</code> class and set a default
layout name for that module (so different sections of your homepage could be organized into
modules with different layouts/designs all over).
<pre class="sh_xml">
<!-- app/config/output_types.xml example part: -->
<layout name="default">
<layers>
<layer name="content"/>
<layer name="decorator">
<slots>
<slot name="header" module="Default" action="Header" />
<slot name="navigation" module="Default" action="Navigation" />
<slot name="footer" module="Default" action="Footer"/>
</slots>
<ae:parameters>
<parameter name="template">%core.template_dir%/decorators/DefaultMaster</ae:parameter>
</ae:parameters>
</layer>
</layers>
</layout>
<layout name="another">
<layers>
<!-- xinclude layer definitions from default layout (should use better xpath expression and 1.0 style namespaces...) -->
<xi:include xpointer="xmlns(agavi=http://agavi.org/agavi/1.0/config)
xpointer(//agavi:output_type[@name='html']/agavi:layouts/agavi:layout[@name='default']/agavi:layers/*)" />
</layers>
</layout>
</pre>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_18">How to re-use input templates to display errors?</dt>
<dd>Without knowing the specific requirements of your application it is usually a good practice to have a
setup of three views for your input form handling action: <strong>Success</strong>View,
<strong>Error</strong>View and <strong>Input</strong>View. You would appoint
each of those views depending on the state or the validation results. "Input" is
used to display the empty form, "Error" is used in case any validation or other
errors occur and "Success" will be appointed after handling data successfully.
You may use the Agavi action wizard to create the necessary files for you:
<pre>
$ dev/bin/agavi action-wizard
Module name: Posts
Action name: Add
Space-separated list of views to create for Add [Success]: Input Success Error
</pre>
Your code (pseudo-style) could then look something like this:
<pre class="sh_php">
// in the input handling action (e.g. Default_AddFoobarAction):
public function executeWrite(AgaviRequestDataHolder $rd)
{
// ... some code ... update via models etc.
if ($errors_while_trying_to_do_something_useful)
{
return 'Error';
}
return 'Success';
}
public function handleError() // could be handleWriteError or similar
{
// error handling like (p)re-populating the form
return 'Error';
}
public function getDefaultViewName()
{
// by default show the input template
return 'Input';
}
// in the input and success views:
public function executeHtml(AgaviRequestDataHolder $rd)
{
// set some attributes etc. for your template
}
// in the error view
public function executeHtml(AgaviRequestDataHolder $rd)
{
// re-use the input template for presenting the errors in the form
$this->getLayer('content')->setTemplate('Input');
}
</pre>
By re-using the input template in your error view you can avoid duplicating
code without sacrificing later maintainability. You shouldn't go for having
less and less files in these cases, as you'll notice later on, that you need
to add views and templates when you add other output types. Without proper
structuring you would need to write a lot of unmaintainable spaghetti code.
<div class="hint">
A common <a href="http://en.wikipedia.org/wiki/Code_smell" title="Go to Wikipedia entry about code smells...">code smell</a>
when using Agavi would be too large classes/methods for your actions and views.
It's pretty much time for refactoring when your files grow up to hundreds of lines.
Try to make better use of the built-in validators or write custom validators to
save on unnecessary validation logic in your actions. Use the <a href="#filter_0">FormPopulationFilter</a>
to pre- and re-populate your forms. Outsource code to different AgaviModel classes.
Strive to have lean actions/views/templates by hiding business logic in those (re-usable!)
model classes. Prevent duplicated code in templates and views by creating simple
'wizard like' actions to display given data. See the FAQ entries about
<a href="#general_16">slots</a> and <a href="#general_4">simple actions</a>
for further ideas.
</div>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_19">How to load another master template?</dt>
<dd>Setting another master template is the same as setting a normal template
to use. Just use the apropriate layer (<code>decorator</code> in this case)
in the view:
<pre class="sh_php">
$this->getContext()->getLayer('decorator')->setTemplate(AgaviConfig::get('core.templates').'/some_other_master');
</pre>
How to change layouts using <code>output_types.xml</code> is described under
<a href="#general_17" title="go to answer 17">How to load different layouts?</a>.
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_20">What is the Agavi startup sequence?</dt>
<dd>Agavi starts something like that:
<ol>
<li>DatabaseManager (init + startup)</li>
<li>LoggerManager (init + startup)</li>
<li>TranslationManager (init)</li>
<li>Request (init)</li>
<li>Routing (init)</li>
<li>Controller (init)</li>
<li>Storage (init + startup)</li>
<li>User (init)</li>
<li>TranslationManager (startup)</li>
<li>User (startup)</li>
<li>Routing (startup)</li>
<li>Request (startup)</li>
<li>Controller (startup)</li>
</ol>
For used classes and startup sequence e. g. see the <code>AgaviFactoryConfigHandler</code> source file. The shutdown
sequence is somewhat in reverse. Controller is shutdown first, then request, routing, user, translation manager,
storage, logger manager and finally the database manager.<br>
The startup sequence is important, as this means, that you are able to e.g. access the user in routing and
validation if you need it. Usually <code>$_GET etc.</code> are unset in <code>AgaviRequest::startup()</code> and/or
<code>AgaviWebRequest::startup()</code>, but with the above startup sequence you are able to register
<code>$_GET etc.</code> as a routing source in <code>init</code>. To disable certain functionalities of
Agavi just look at <code>settings.xml</code>:
<pre class="sh_xml">
<setting name="use_database">true</setting>
<setting name="use_logging">true</setting>
<setting name="use_security">false</setting>
<setting name="use_translation">true</setting>
</pre>
To change the implementing classes have a look at the <code>factories.xml</code> file. Please be careful, when you
enable or disable features in settings, as you won't be able to access certain features. For example the
<code>AgaviDateTimeValidator</code> needs the translation manager to work correctly.<br>
In case you want to disable the logging manager you should better get used to write your logging code like this:
<pre class="sh_php">
if (AgaviConfig::get('core.use_logging', false))
{
// do your logging...
}
</pre>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_21">What do I need to know about Agavi models and how to use them?</dt>
<dd>In patterns such as MVC, <em>data models</em> are widely used to deal with the "business logic" of an application. The way you do this, or even, what patterns to integrate is completely <b>up to you</b>. Nonetheless, Agavi <em>models</em> are extremely useful for a good number of reasons, so you'll probably want to use/extend them with your classes. The AgaviModel class creates a <em>bridge</em> between the business logic, and the rest of the application logic, by simply using Agavi </em>$context</em> internally.
<pre class="sh_php">
public function initialize(AgaviContext $context, array $parameters = array())
{
$this->context = $context;
}
</pre>
<p>Every time you initialize a model a $context parameter is passed, along with an array of custom params. This is the way to do it:</p>
<pre class="sh_php">
$this->getContext()->getModel('Model_Name', 'Module_Name', array('foo','bar'));
</pre>
<p>Models are loaded in the way shown above, and additionally, you can skip the second parameter ONLY if the model is not inside any module, but located at the main application's models/ folder (at the lowest level). Bare in mind not to use the word 'Model' when passing the first parameter. It's '<strong>FooBar</strong>', not '<strong>FooBarModel</strong>'.</p>
<p>Here's a few extra things you need to know about Agavi models:</p>
<ul>
<li>Models are only loaded once.</li>
<li>Models do not need to be included in autoload.xml, as long as they are under the models folder ( app/models/ ).</li>
<li>Models implement __sleep and __wakeup methods, therefore they are easily transform into serialized objects for caching and other common uses.</li>
<li>When using getModel(), a model's constructor is called as well (if the model has one) which might lead into some confusion. So, if needed you may write both, a constructor and a initialize method. Only the later will have access to the context though.</li>
</ul>
</dd>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_22">How to use custom configuration files?</dt>
<dd>There may always be situations where you need complex configuration settings for certain taks,
that you don't want to add to the default <code>settings.xml</code> file or include via PHP, YAML,
INI files or whatever other formats you like. Agavi gives you a very convenient way to use custom
configs and have them validated and cached for further requests like normal Agavi config files.
Just start by defining a new config handler in your app's <code>config_handlers.xml</code> file:
<pre class="sh_xml">
<handler pattern="%core.config_dir%/project/foo/*.xml" class="AgaviReturnArrayConfigHandler" />
</pre>
This line tells Agavi to use the built-in (yet <strong>deprecated</strong>) <code>AgaviReturnArrayConfigHandler</code> class
for all XML files to be found in the <code>app/config/project/foo/</code> directory.<br>
To use the cached config file in your actions, views, models or other classes do something like:
<pre class="sh_php">
$config = include AgaviConfigCache::checkConfig(AgaviConfig::get('path_to_one_of_the_xml_configfiles'));
</pre>
The above defined handler parses the XML file and returns an associated array of all defined tags and values
for easy access. You may use the config like this:
<pre class="sh_php">
if (!isset($config['some']))
{
throw new LogicException('element "some" missing!');
}
foreach ($config['some']['bar'] as $foo => $data)
{
$this->doSomethingWithEach($foo, $data);
}
</pre>
The XML file for the above pseudo code could be like that:
<pre class="sh_xml">
<?xml version="1.0" encoding="UTF-8"?>
<some>
<baz>asdf</baz>
<foo>-1</foo>
<bar>
<some>...more data and xml...</some>
<more>...more data and xml...</more>
<deep>...more data and xml...</deep>
<structs>...more data and xml...</structs>
</bar>
</some>
</pre>
To be able to write code without all the validational <code>isset()</code> etc. statements
you may validate your XML structure against a well defined XML schema definition (XSD) and
use your own custom config handlers. Create a schema file, define and create your
config handler class and add it to <code>autoload.xml</code>:
<pre class="sh_php">
class FooDefinitionConfigHandler extends AgaviXmlConfigHandler
{
// have a look at AgaviXmlConfigHandler and AgaviIXmlConfigHandler
// and then
// implement necessary methods and validate your XML structure...
}
</pre>
<pre class="sh_xml">
<handler pattern="%core.config_dir%/project/foo/*.foo.xml" class="FooDefinitionConfigHandler">
<validation>%core.config_dir%/xsd/foo_definition.xsd</validation/>
</handler>
</pre>
For examples of XSDs and custom config handler classes just have a look at the Agavi source code.
<em>In case you don't want to use Agavi's XML parser, you'll need to resolve parents, filter content
and order <configuration> blocks by contexts and environments all by yourself (see
<code>AgaviXmlConfigParser</code>).</em>
For further information and if you wonder what's possible, please head to the API docs or ask the devs.
To quote the configuration section of the <a href="http://svn.agavi.org/trunk/RELEASE_NOTES-1.0">Agavi 1.0
RELEASE_NOTES file</a> extensively:
<blockquote>
<p>Agavi now supports any number of XML Schema (including XML Schema Instance declarations), RELAX NG
or Schematron files to be validated against config files or merged result documents in several different
stages, can apply XSL transformations (including those declared in <?xml-stylesheet?> PIs) and has
extended namespaces support that allow backwards-compatibility to old config files in future versions.
New-style configuration handlers extending AgaviXmlConfigHandler are now given a single AgaviXmlConfigDomDocument
(extends DOMDocument) instance to work on, which already holds only the relevant <configuration> blocks
in the correct order. Together with other subclasses of ext/dom classes, it provides several convenience methods
to ease the processing of configuration files, but of course, all capabilities of DOM, including XPath,
are usable inside configuration handlers.</p>
</blockquote>
<div class="hint">A more in-depth article making extensive use of the Agavi custom configuration handlers was written
by Luis Merino. See <a href="http://www.luismerino.name/delivering-static-files-helping-your-app-scal"
title="See blog article about custom configuration files in Agavi...">Delivering static files:
Helping your app scale using custom configs in Agavi Framework</a> for an introduction about his concept of
declaratively specifying which static files (e.g. javascript files or stylesheets) to include for matched routes
via a <code>StaticFilesConfigHandler</code>.
</div>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_23">How to use Propel models with Agavi?</dt>
<dd>For a simple explanation on how to include <a href="http://www.propelorm.org/"
title="Homepage of PHP5 Propel Object-Relational Mapping (ORM) project">Propel</a> in your own projects see
<a href="http://blog.veikko.fi/post/82466126/installing-a-project-local-propel"
title="Veikko explains how to include Propel in an Agavi project by getting it via SVN...">this Blogpost</a>.
All you need is <a href="http://www.phing.info/trac/" title="Homepage of The Phing Project">Phing</a> which you
probably already have thanks to Agavi.<br />
In short: Just get Propel from GitHub as Tarball or via <code title="e.g. via: git clone https://github.com/propelorm/Propel.git propel">clone</code>
and put the <code title="Contains the classes required to run Propel in the command line">generator</code> files
into your <code>dev/libs/propel</code> folder while the <code title="Contains the classes required to access Propel models and the database.">runtime</code>
files may be put into your project's <code>app/lib/vendor/propel</code> directory. Use this distinction when you
don't need the generator files in production environments.<br />
After getting Propel run it in your project's directory via <code>dev/libs/propel/bin/propel-gen dev/db</code>. Generate
your runtime config files into the <code>dev/db</code> directory via <code title="dev/libs/propel/bin/propel-gen convert-conf">convert-conf</code>
command. Your classes will probably be in your <code>dev/db/build</code> directory. The important files in your <code>dev/db</code> folder
are <code>schema.xml</code>, <code>runtime-conf.xml</code> and <code>build.properties</code>. Configure your directories similar to this:
<pre class="sh_ini">
#relative to propel-gen script
propel.output.dir = ../../..
propel.php.dir = ${propel.output.dir}/app/lib/vendor/propel
propel.phpconf.dir = ${propel.output.dir}/dev/db/config
propel.sql.dir = ${propel.output.dir}/dev/db/sql
</pre>
Configure your include path to include the Propel runtime and OM classes.
<pre class="sh_php">
$path = '/your/project/libs' . PATH_SEPARATOR . '/your/project/app/lib/vendor/propel';
set_include_path(get_include_path() . PATH_SEPARATOR . $path);
</pre>
You may setup your Propel connection in your <code>app/lib/config/database.xml</code> file similar to this:
<pre class="sh_xml">
<database name="propelom" class="AgaviPropelDatabase">
<ae:parameter name="config">%core.app_dir%/config/your-project-conf.php</ae:parameter>
</database>
</pre>
You may generate the classmap and conf files to the wanted directory or just copy <code>classmap-your-project-conf.php</code> and
<code>your-project-conf.php</code> (from Propel's <code>dev/db/build/conf</code>) to Agavi's <code>app/config</code> directory. You
can of course <a href="#general_10" title="FAQ entry about defining custom database connection settings">define custom database connection overrides</a> when needed.
<div class="hint">
Remember to configure your DB connections for all environments and that you have set <code>use_database</code> to
<code>true</code> in your <code>app/config/settings.xml</code>.
</div>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_24">How to change the template lookup patterns Agavi uses?</dt>
<dd>You can customize the default pattern Agavi uses to find templates in the filesystem by overriding the
<code>targets</code> parameter on a layer in <code>output_types.xml</code>. This enables your application to
have different templates and let Agavi choose the first one that matches the given targets. The following
snippet gives an example on how one could implement mobile templates that reuse existing actions and views by
letting Agavi first look for <code>Success.mobile.php</code> before trying to use <code>Success.php</code>.
That way you can have a mobile version of your website where it is not strictly necessary to duplicate all templates
for the mobile version as you can reuse the original templates. In case you want to adjust a template for mobile
devices you can just put a <code>*.mobile.php</code> file next to the existing normal one. The <code>{extension}</code>
placeholder is usually set via the <code>default_extension</code> parameter of the renderer used for the layer.
<pre class="sh_xml">
<layer name="content">
<parameters>
<parameter name="targets">
<parameter>${directory}/${template}.mobile${extension}</parameter>
<parameter>${directory}/${template}${extension}</parameter>
</parameter>
</parameters>
</layer>
</pre>
<a href="#faq" title="Back to top">↑</a></dd>
<dt id="general_25">How to use different renderers for different layers of output types?</dt>
<dd>You can set a different renderer for each of the layers you define in <code>output_types.xml</code> or set dynamically
within views:
<pre class="sh_xml">
<layer name="user_generated_content" renderer="twig" />
</pre>
The above feature enables you to define specific layouts with different layers where e.g. all layers are rendered via a
<code>AgaviPhpRenderer</code> while the layer that displays sensitive user provided content is rendered using a template
engine renderer of your choice that has features such as sandboxing or limited sets of capabilities. The given example
uses a renderer named <code>twig</code> (see <code>AgaviTwigRenderer</code> and the <a href="http://twig.sensiolabs.org/"
title="See the Twig homepage for usage examples and feature documentation...">Twig homepage</a> for more information) that
is usually defined for specific output types. With this knowledge you could do crazy things like XML or Javascript islands
within HTML all by defining according layers with appropriate custom renderers that handle escaping or whatever.<br />
The following example defines three renderers. Two of them are provided by Agavi itself while the third one is a custom renderer
that DracoBlue and me wrote as a small test to make transitioning an existing Agavi application from using PHP as its templating
engine to allowing the use of Twig templates where needed. For refactoring reasons this enables you to start using Twig in
templates one template at a time while the fallback will always be the PHP template variant.
<pre class="sh_xml">
<output_type name="html">
<renderers default="proxy">
<renderer name="proxy" class="AgaviProxyRenderer">
<ae:parameter name="renderers">
<ae:parameter>twig</ae:parameter>
<ae:parameter>pphp</ae:parameter>
</ae:parameter>
</renderer>
<renderer name="pphp" class="ProjectPhpRenderer">
<ae:parameter name="assigns">
<!-- default assigns of Agavi omitted -->
</ae:parameter>
<ae:parameter name="default_extension">.php</ae:parameter>
<ae:parameter name="var_name">t</ae:parameter>
</renderer>
<renderer name="twig" class="AgaviTwigRenderer">
<ae:parameter name="assigns">
<-- default assigns of Agavi omitted -->
</ae:parameter>
<ae:parameter name="default_extension">.twig</ae:parameter>
<ae:parameter name="extract_vars">true</ae:parameter>
<ae:parameter name="options">
<ae:parameter name="auto_escape">true</ae:parameter>
<ae:parameter name="auto_reload">true</ae:parameter>
</ae:parameter>
</renderer>
</renderers>
<layouts default="standard">
<!-- standard layout with a content and a decorator layer -->