2009年3月1日星期日

Re: [fw-mvc] Application structure using modules - here's ours

Hi Vincent and others,

We have built an app development framework based on Zend+Doctrine+Smarty+other stuff that may be of interest to you, and I'll share our structure here.   I'll say upfront that this is a fairly heavy-weight approach which is useful for development shops that implement similar technology over and over, and is not suited to a one-off site implementations.

We use modules, but we also go much further.   Unlike most implementations I've seen, we have separated out our framework libraries from the application, so we can build multiple applications which reference the one shared library.  This makes bulk deployment and bulk upgrades faster and easier.

We build lots of sites with similar characteristics and we favour heavy shared library code and light per-application code.  We don't like embedding the framework libs into each project, nor do we like code re-use via copy/paste.  

So, in our framework we have added modules into both our framework library, and into each application, so commonly-deployed modules are shared, not copied.

An example server set-up.

At the server level, you a sample deployment might be something like this:
/app1.example.com
/app2.example.com
/common 

Against Zend conventions, our docroot is each /app* directory.   We found that it was too restrictive using a /public folder, as we could not store the module's assets (style sheets, images, etc) in the module's directory.  Instead, we have a modified .htaccess file that provides protection against source file access, and allows each module to access its assets via HTTP.   

I'd be interested in hearing from any other people that have solved this in other ways.

Applications
Each app is its own virtual server in apache, and as explained above the app root is also the doc root for us.  Each application looks like:

/config
/library
/modules
/tests
/themes
/tmp


/library
Our library has our model classes (named as a singular because the entire set of classes is considered as the model).  This uses Doctrine for ORM, but can also contain other hand-written classes for factories, etc.   We also have the ability for per-application view and controller helpers (although in practice we tend to put these into our framework library for re-use across all our applications). 

   /model
   /helper
      /controller
      /view


/modules
Each module directory looks like this:

/module1
   /controllers
       FooController.php
       BarController.php
   /library
      Foo.php
   /public
      foo.jpg
   /helpers
   /filters
   /views
      /foo
         index.tpl
      /bar
         index.tpl
         bleh.tpl
   /tests


Of special note with our module implementation:
- we've changed Zend's /views directory structure to be flatter (removed the /scripts dir)
- we have a /public directory in each module where we can place public assets.  We then use rewrite rules to allow HTTP access to that dir while restricting access to others.  This allows modules to have images, stylesheets, etc in each module.
- we have per-module tests

/tests
We have per-app tests here, using PHP_Unit.

/themes
We have extended Zend_Layout to accommodate the idea of multiple "themes", with each theme containing multiple layouts.  So, inside /themes you may find:

/theme1
   /controllers
      DefaultController.php
      HomeController.php
   /lib
   /helpers
   /filters
   /public
      foo.jpg
   /views
      /default
         layout.tpl
      /home
         layout.tpl

We have heavily extended Zend_Layout to do the following:
- our layouts run a full dispatch against a layoutAction() so we can build a controller and assign things into the layout's view.
- each theme can have its own /lib, /helpers, /filters, etc so they are logically separate from each other.   To understand why, the broadband theme may need lots more helpers to do stuff in their sidebar, whereas a wap theme won't have a sidebar but it might need a helper to look up GPS coords from the client.  This approach allows for contextualised themes or subsite themes, and a layout within a theme can have its own logic to apply to its view, rather than making those decisions in the module's controller.
- There's lots more I could explain here - it's pretty cool stuff - but it's out of scope for a discussion on directory structures.

/tmp
Our tmp dir has subdirectories for /logs, /cache and /smarty.


The common library

Our library has its own set of modules and themes.  This allows us to provide a central CMS via a set of modules which is delivered over each application's URL namespace (for example, app1.example.com/cms loads the 'cms' module which is stored in our library, not in the application).

The library directory looks like this:
/library
   /ZendFramework
   /Smarty
   /Doctrine
   /Bravo
   ...
/modules
   /cms
   ...
/public
   foo.jpg
/scripts
/tests
/themes


This approach is certainly not for everyone.  It has these advantages:
- separate libraries for all applications, each application, each module to store models, helpers, etc.  
- allows for extensive code sharing across multiple similar applications (such as a CMS) while maintaining access to the app's model classes
- CVS/SVN checkins per application are lighter as we do not include shared libs
- quick to deploy solutions to shared server environments once they are configured.

We've also modified much of Zend's MVC with different routes, layouts, views, helpers, etc.  We chose Zend because it was not a full app development environment like Symfony or Code Igniter, but was instead much more flexible, allowing us to create our own customised development environment that perfectly suits our company. 

Hope this helps...

Cheers, Scott

                                                                                                                 
Scott Davey » Datalink: creative e-business
www.datalink.com.au | 1300.DATALINK | Melbourne, Australia



On 27/02/2009, at 9:52 PM, vadim gavrilov wrote:

Thanks for that. Did anyone made something that uses Modules? Not just controllers in a single directory?

Thanks.

On Fri, Feb 27, 2009 at 12:17 PM, Robert Castley <Robert.Castley@macro4.com> wrote:
 
- Robert


From: vadim gavrilov [mailto:vadimg88@gmail.com]
Sent: 26 February 2009 18:08
To: keith Pope; fw-mvc@lists.zend.com; Zend Framework - General
Subject: [fw-general] Re: [fw-mvc] Application structure

Would appreciate any others sharing there structure and/or ideas of application structures.

Thanks.

On Thu, Feb 26, 2009 at 3:14 PM, keith Pope <mute.pop3@googlemail.com> wrote:
The source for my forthcoming book is available here:

http://code.google.com/p/zendframeworkstorefront/

This will be updated soon so that is uses the Zend_Application though

2009/2/26 vadim gavrilov <vadimg88@gmail.com>:
> Was wondering if anyone would share his/her structure for there application.
> And if possible the code they used. I am just interested in seeing how
> others write down there application structure and code. With the routers if
> anyone uses, Modules and such.
>
> Thanks.
>
> --
> Vincent Gabriel.
> Lead Developer, Senior Support.
> Zend Certified Engineer.
>
>
>
>
>



--
----------------------------------------------------------------------
[MuTe]
----------------------------------------------------------------------



--
Vincent Gabriel.
Lead Developer, Senior Support.
Zend Certified Engineer.





________________________________________________________________________
This email has been scanned for all known viruses by the MessageLabs Email Security Service and the Macro 4 plc internal virus protection system.
________________________________________________________________________

________________________________________________________________________
This email has been scanned for all known viruses by the MessageLabs Email Security Service and the Macro 4 plc internal virus protection system.
________________________________________________________________________



--
Vincent Gabriel.
Lead Developer, Senior Support.
Zend Certified Engineer.





Re: [fw-db] Zend_Db_Table_Row::save() and transactions...

To me it would seem a good idea to put the entire transaction bloc
inside the try block, this way in the catch block you'll pay a bit
more attention on the objects that need to be discarded. As said
before making the framework to clean your mess would have too many
unwanted consequences, for example if the transaction fails u might
want to take the data from the invalid objects and make something
useful with it, but if the framework would clean your objects, you
would have to start over.
Make sure that you know how your data looks after a catch block in
order to avoid bugs as much as possible (adding $user = null in the
catch block could be an example of knowing what data u must deal with
after the try-catch block)

On Thu, Feb 26, 2009 at 17:14, Lucas Corbeaux <lucas.corbeaux@gmail.com> wrote:
> I know that "linking" a Zend_Db_Adapter and a Zend_Db_Table_Row in this way
> seems a really bad idea, but as I don't consider myself as a Zend Framework
> expert, as I don't work enough with it yet, I'll just ask if some peoples
> other than me was a bit surprised by this behaviour, and if something is
> planned to correct it in the future of if it remains a bad idea.
>
> I'll don't extends my own class for the reasons you said: I'll surely
> encounters huges sides effects that I can't, right now, foresee. As I can't,
> I ask what other people think about it, and if I still believe that in a
> perfect world a transaction rollback would undo some save methods sides
> effects (as generating primary ket), I'm also aware that it could be
> impossible to do this a right and harmless way.
>
> william0275 a écrit :
>>
>> Lucas Corbeaux wrote:
>>
>>>
>>> I must admit I don't really agree.
>>>
>>> The natural behaviour of a transaction seems to me that all happen
>>> atomically, and an ORM would, in my sense, be aware of what happened in the
>>> database layer, whenever it's possible.
>>> I'm totally aware that a feature like this will broke the object design
>>> of the Zend_Db module (because that mean store a Zend_Db_Table_Row
>>> references for "active" rows during a transaction in the Zend_Db_Adapter
>>> class), but forcing the programmer to be aware of the exact inner mechanism
>>> of the framework don't seem to me a good thing, even if I personnally prefer
>>> to look at the source code every time I need.
>>>
>>>
>>>
>>
>> But think about it. You instantiate a *new* Zend_Db_Row using
>> Zend_Db_Table->createRow(), and apply a save() on it. What would you
>> expect
>> a framework to do when you rollback a transaction? Call some unsave() or
>> undo() method on your Zend_Db_Table_Row? I think if you rely on the
>> framework to do your own cleaning, you're going to have other problems
>> than
>> a simple Zend_Db_Row filled with an invalid primary key... Also, what
>> happens when you're working on a Zend_Db_Table_Rowset which contains a row
>> that should disappear after a rollback? You would like the framework to
>> automatically remove it from the rowset?
>> Anyhow, it could be an endless debate. You could of course extend your own
>> class that would do this for you, but I for sure would not want this in
>> the
>> native framework, it would add too much complexity and potential problems.
>>
>
>

Re: [fw-mvc] Proper use of Zend_Loader_Autoloader_Resource

-- Cristian Bichis <contact@zftutorials.com> wrote
(on Sunday, 01 March 2009, 09:05 PM +0200):
> Matthew Weier O'Phinney wrote:
>
> Regarding the settings in questions 1, 3, and 4, these could be in a
> config file as well, and you could simply pass the path to a config file
> when instantiating Zend_Application. As an example, consider this:
>
> $app = new Zend_Application(
> APPLICATION_ENV,
> APPLICATION_PATH . '/configs/site.ini'
> );
>
> where APPLICATION_PATH . '/configs/site.ini' reads as follows:
>
> [production]
> phpSettings.display_errors = false
> phpSettings.error_reporting = 771
> autoloaderNamespaces.imagis = "Imagis_"
> autoloaderNamespaces.phly = "Phly_"
> includePaths.cwd = "."
> includePaths.lib = APPLICATION_PATH "/../library"
>
> [development : production]
> phpSettings.display_errors = true
> phpSettings.error_reporting = 8191
>
> As you can see, this makes the application code simpler, and makes it
> easier to switch settings per environment (which is also one of the
> goals of Zend_Application).
>
>
>
> Thanks for replies,
>
> I have one more question:
>
> It is possible to set bootstrap file from config.ini ?
>
> //index.php
> $application = new Zend_Application(APPLICATION_ENVIRONMENT, APPLICATION_PATH
> . '/config/config.ini' );
>
> //config.ini
> bootstrap = APPLICATION_PATH.'/Bootstrap.php'

Try just this:

bootstrap = APPLICATION_PATH "/Bootstrap.php"

note the lack of "." and the double quotes. Also, ensure that the
APPLICATION_PATH constant is set prior to instantiating
Zend_Application.

> error:
> Warning: require_once(APPLICATION_PATH.'/Bootstrap.php') [function.require-once
> ]: failed to open stream: No such file or directory in D:\_Work\hostexpert2\
> library\dasprid\Zend\Application.php on line 228
>
> In am encountering error from above when trying to set bootstrap through config
> file.

--
Matthew Weier O'Phinney
Software Architect | matthew@zend.com
Zend Framework | http://framework.zend.com/

Re: [fw-mvc] Proper use of Zend_Loader_Autoloader_Resource

Matthew Weier O'Phinney wrote:
 Regarding the settings in questions 1, 3, and 4, these could be in a config file as well, and you could simply pass the path to a config file when instantiating Zend_Application. As an example, consider this:      $app = new Zend_Application(         APPLICATION_ENV,          APPLICATION_PATH .  '/configs/site.ini'     );  where APPLICATION_PATH . '/configs/site.ini' reads as follows:      [production]     phpSettings.display_errors = false     phpSettings.error_reporting = 771     autoloaderNamespaces.imagis = "Imagis_"     autoloaderNamespaces.phly = "Phly_"     includePaths.cwd = "."     includePaths.lib = APPLICATION_PATH "/../library"      [development : production]     phpSettings.display_errors = true     phpSettings.error_reporting = 8191  As you can see, this makes the application code simpler, and makes it easier to switch settings per environment (which is also one of the goals of Zend_Application).    
Thanks for replies,

I have one more question:

It is possible to set bootstrap file from config.ini ?

//index.php
$application = new Zend_Application(APPLICATION_ENVIRONMENT, APPLICATION_PATH .  '/config/config.ini' );

//config.ini
bootstrap = APPLICATION_PATH.'/Bootstrap.php'

error:
Warning: require_once(APPLICATION_PATH.'/Bootstrap.php') [function.require-once]: failed to open stream: No such file or directory in D:\_Work\hostexpert2\library\dasprid\Zend\Application.php on line 228

In am encountering error from above when trying to set bootstrap through config file.

--  Best regards, Cristian Bichis www.zftutorials.com | www.zfforums.com | www.zftalk.com | www.zflinks.com

Re: [fw-core] Zend Form Element, set name allowBrackets

-- Mikael Abrahamsson <micke@gapps.whitefish.se> wrote
(on Sunday, 01 March 2009, 11:15 AM +0100):
> When creating a new instance of a zend form element how to you give it a
> name with brackets?
> The Zend_Form_Element::setName() method calls the
> Zend_Form_Element::filterName() method which taks as a second argument
> $allowBrackets boolean.
>
> So how do I set the name with brackets?

So, first off, names are not allowed to have brackets internally so that
a variety of other features will work (overloading access, primarily).

That said, you *can* force brackets to appear in the output in a couple
of different ways.

* If you want brackets for allowing multiple values to be captured --
i.e., a name like 'foo[]' -- turn on the isArray property:

$element->setIsArray(true); // or pass a true value to the
// "isArray" key during
// instantiation

* If you want the value to be a key in another value, e.g.,
"bar[foo]", then you need to tell the element it belongs to another
value:

$element->setBelongsTo('bar'); // or pass the value to the
// 'belongsTo' key during
// instantiation

* If you use sub forms, array notation happens by default; all
elements "belongTo" the name of the sub form


--
Matthew Weier O'Phinney
Software Architect | matthew@zend.com
Zend Framework | http://framework.zend.com/

[fw-core] Zend Form Element, set name allowBrackets

When creating a new instance of a zend form element how to you give it
a name with brackets?
The Zend_Form_Element::setName() method calls the
Zend_Form_Element::filterName() method which taks as a second argument
$allowBrackets boolean.

So how do I set the name with brackets?

Re: [fw-db] Column ambiguously defined

Hi,

For Oracle (and DB2), there is no LIMIT clause. So ZF emulate this on
the fly:

SELECT "INDIVIDUS".*, "AFF".*
FROM "EAI_TA_INDIVIDUS" "INDIVIDUS"
INNER JOIN "EAI_TA_AFFECTATIONS" "AFF" ON INDIVIDUS.IND_ID = AFF.IND_ID
WHERE (INDIVIDUS.ANN_ANNEE = 2009) AND (INDIVIDUS.IND_MATRICULE = '17483')

becomes:

SELECT z2.*
FROM (
SELECT z1.*, ROWNUM AS "zend_db_rownum"
FROM (
SELECT "INDIVIDUS".*, "AFF".*
FROM "EAI_TA_INDIVIDUS" "INDIVIDUS"
INNER JOIN "EAI_TA_AFFECTATIONS" "AFF" ON INDIVIDUS.IND_ID =
AFF.IND_ID
WHERE (INDIVIDUS.ANN_ANNEE = 2009) AND (INDIVIDUS.IND_MATRICULE =
'17483')
) z1
) z2
WHERE z2."zend_db_rownum" BETWEEN 1 AND 1

In this case Oracle (or DB2) is not able to resolve z1.*

We have 2 failures in ZF unit tests for this (see ZF-3168's comments):
2)
testTableRelationshipFindManyToManyRowsetSelect(Zend_Db_Table_Relationships_OracleTest)
3)
testTableRelationshipMagicFindManyToManyRowsetSelect(Zend_Db_Table_Relationships_OracleTest)

A rapid workaround is to realize a fetchAll()->current() instead of
fetchRow().

Mickael

Thomas VEQUAUD a écrit :
>
> Trust me I tried everything with Zend and nothing works.
> Actually, it's a bug with Oracle database :
> http://framework.zend.com/issues/browse/ZF-3168
>
> I have to deliver my web application for Monday... Bad weekend expected!
>
> --
> Thomas VEQUAUD http://thomas.vequaud.free.fr/
> Expert EPITECH en Ingénierie Informatique
> Tél : +33(0)6.50.39.28.10 Fax: +33(0)9.58.46.10.07
>
>
> On Fri, Feb 27, 2009 at 11:40 PM, PHPScriptor <contact@phpscriptor.com
> <mailto:contact@phpscriptor.com>> wrote:
>
>
> Oh yeah, I see it, didn't look good enough... anyway. You should
> rename one
> of the id's.
>
> e.g. the one from INDIVIDUS: IND_ID AS BLABLA. But better I think, you
> should only let the select, select one of the two id's.
>
> e.g.
> $select->joinInner(array("AFF" => "EAI_TA_AFFECTATIONS"),
> 'INDIVIDUS.IND_ID = AFF.IND_ID',array('field1','field2','field3));
> so the 3the parameter is the fields you want to select from this
> table.
> Select them all but not IND_ID.
>
> http://www.phpscriptor.com/tutorial/zend/zend.db.select.html#zend.db.select.building.join
> http://www.phpscriptor.com/tutorial/zend/zend.db.select.html#zend.db.select.building.join
>
>
> Thomas VEQUAUD wrote:
> >
> > Yep I have an IND_ID in each tables... That's why I don't
> understand this
> > issue...
> > If you've got another idea... Just show me the right way! ;)
> >
>