Kohana 3: Organize Classes (MVC) in Subfolders - php

i was wondering how to organize and name my classes
The goal:
classes
books
controller
model
view
authors
controller
model
view
// thanks for helping

I think you'll need to use the modules folder.
A module is like a mini Kohana application, for example, a simple module might have
/modules/first_module/...
config/
classes/controller/
classes/model/
views/
However if that isn't what you want, just follow the same directory structure above under the application folder.
Update
Okay, so if you want a book set of controllers, you may lay it out like so (if you wanted them in sub folders)... (example only...)
controllers/books/base.php
class Controller_Books_Base extends Controller {
}
controllers/books/fiction.php
class Controller_Books_Fiction extends Controller_Books_Base {
}
And a route like so in bootstrap.php
Route::set('books_fiction', 'fiction-books/<action>/<id>', array('action' => '(create|read|update|delete)', 'id' => '\d+')
->defaults(array(
'controller' => 'Books_Fiction'
));
Some example classes for Books and Authors.
class Model_Book {
// crud functions
}
class Model_Author {
// crud functions
}
class Controller_Book {
public function view($id) {
$book = Model::factory('Model_Book')->get($id);
$this->template->bookDetails = $book;
}
}
That will hopefully get you started.

Related

Rendering a view file in self made PHP MVC framework

I've developed My Own MVC Framework using php.
I call view files in controller like:
include('../view/home.php');
but I want to use it like:
$this->view('home');
How can I define common function for that where I can just pass view name i.e home only and it will do include view file without passing the full file path?
No one could answer you without seeing your codes really. But this should be my approach.
You should have a class that all your controllers extend. Lets say that you have class Controllers and all your controllers extend it.
Then you may have a method inside the class named view($view_name).
public function view($view_name){
include $some_path . '/' . $view_name . '.php';
}
then whenever you call view by $this->view it will include the view if it exists.
This is not the best approach and I did not test the code. I just wanted to show you the path
I don't know Your MVC file/directory/namespace and etc structure.
But for beginner who tries to learn MVC and Frameworks by "reinventing wheel" (: I can give such example:
1) Create common abstract controller class in app/controllers folder:
namespace App\Controllers;
abstract class Controller {
public function view($name) {
include(__DIR__.'/../views/'.$name.'.php';
}
}
2) Create Your own controller i.e. PagesController and use it:
namespace App\Controllers;
class PagesController extends Controller {
public function home() {
$this->view('home');
}
}
p.s. You may omit namespace-ing, abstract word depending on autoloader logic

create shared view model in zend framework 2 project

I want to assign generic variables to the view renderer outside the controller action.
I'd prefer to handle this in the module class bootstrap.
My question is, how do I create a view model in the module class bootstrap that can be shared with the controller .
My end result is to have the ability to add variables to the view model before we create a new instance of one inside the controller action.
Here's something I started on, however i cannot add variables to the declared viewmodel and have it persist to the controller's new instance of a view model.
Is there a way to create a view model and have it set as the renderer before dispatch.
Here's something i started but if i can get it in the module class bootstrap instead id prefer that.
I dont think this works though.
class BaseController extends AbstractActionController
{
protected $viewModel;
public function onDispatch(MvcEvent $e)
{
$this->viewModel = new ViewModel([
'module' => 'modulename',
'controller' => 'controllername',
'action' => 'actionname'
]);
parent::onDispatch($e);
}
}
In the Module.php you have access to the event object.
In this event you can set some variables like this :
$event->getViewModel()->setVariable('isAdmin', $isAdmin);
$event->getViewModel()->setVariable('var', $var);
$event->getViewModel()->setVariable('form', $form);
$event->getViewModel()->setVariable('uri', $uri[0]);
If you want to test more you can also do :
$widgetTemplate = new ViewModel();
$widgetTemplate = $widgetTemplate->setTemplate('widget/container');
$event->getViewModel()->addChild($widgetTemplate, 'widget');
Those variables are available in your layout.phtml. I didn't test if it's the same viewModel available in your controller, give me a feedback if you test this solution.
For a variable defined in module.php, you can also use the Zend\Container\Session component to modify it in controllers

Codeigniter - making my controllers more DRY

In my codeigniter controller function I am using the following code to generate my view and insert all the necessary content:
$left_column = $this->load->view('widgets/sub_navigation', $subnav_data, true);
$left_column .= $this->load->view('widgets/search_box', '', true); //Set data to be loaded into columns
$left_column_base = $this->load->view('widgets/assist_navigation', '', true);
$center_column = 'this is center column';
$right_column = $this->load->view('widgets/ask_us_a_question', '', true);
$right_column .= $this->load->view('widgets/newsletter', '', true);
$right_column .= $this->load->view('widgets/latest_news', '', true);
$this->template->inject_partial('left_column', $left_column); //Inject data into the partial columns
$this->template->inject_partial('left_column_base', $left_column_base);
$this->template->inject_partial('center_column', $center_column);
$this->template->inject_partial('right_column', $right_column);
$this->template->build('template',$data);
I am using a three column layout and the code above dictates what is shown in each of the columns. It works in a very modular way allowing me to customize each page quickly.
Is there a way of simplifying the above code, using arrays maybe, to cut down on repetetive code, making things more DRY??
You need to create base controllers that extend the CI_Controller. Then all your controllers extend a certain base controller you created, depending on what needs to be done in all cases that controller is called.
In application/core create a file called MY_controller.php (the prefix can be changed in config):
class MY_Controller extends CI_Controller {
function __construct()
{
parent::__construct();
/* Widgets are only prepared -- they will be fetched and rendered once layout->render is called.
This saves the overhead of reading the files on requests where layout isn't rendered.
*/
$this->layout->prepare_widget( "widgets/navigation", "navigation_widget" );
$this->layout->prepare_widget( "widgets/footer", "footer_widget" );
$this->layout->prepare_widget( "widgets/twitter", "twitter_widget" );
}
}
class Public_Controller extends MY_Controller {
function __construct()
{
parent::__construct();
}
}
class Admin_Controller extends MY_Controller {
function __construct()
{
parent::__construct();
if( !$this->user->has_permissions( PERMISSION_ADMIN ) )
{
redirect( base_url(), "location", 303 );
die();
}
}
}
class Member_Controller extends MY_Controller {
function __construct()
{
parent::__construct();
if( !$this->user->has_permissions( PERMISSION_REGISTERED ) )
{
redirect( base_url(), "location", 303 );
die();
}
}
}
As you can see, all sub controllers have the widgets automatically because they extend either public, admin or member. A sub controller extending an admin controller has automatically the permissions checked so you don't need to do that ever again. You can apply this concept to your app.
A sub-controller: (placed in the normal application/controllers)
class Articles extends Member_controller {
...
}
Will have automatically ensured that the user is logged in, and the widgets are prepared without doing anything because the parent of parent class already prepared them. All you need to do in articles is to call $this->layout->render if the logic needs layout rendering at the end.
Codeigniter controllers are designed after the transaction script pattern, it's known that controller tend to get large and "not DRY" when you application grows.
To prevent such, you can re-implement the view to handle a two-step compound view pattern supporting layouts. Look for a layout view IIRC there are some on the codeigniter site.
I wrote a blog post explaining my design philosophy on organizing CodeIgniter controllers to make them more DRY. I like to have the index function of my controller serve as a common entry/exit point to avoid repeating operations that are common to all controller methods.
http://caseyflynn.com/2011/10/26/codeigniter-php-framework-how-to-organize-controllers-to-achieve-dry-principles/

How to namespace in a front controller situation using PHP

Alright I have a tiny framework that I hope to open source soon and I'm trying to implement namespacing so that controllers and models don't need appended text. Here's the basic code logic:
url request
htaccess reroutes to index.php which initiates the framework
framework parses route & determines which controller/action to instantiate & fire
the framework's front controller is 'Controller' & project controllers extend 'Controller'
So in order to allow for controllers to be named for example:
class Foo extends Controller {}
and later a model be:
class Foo extends Model {}
My directory structure is like so:
project/
controllers/
foo.php
models/
foo.php
So, I'm obviously needing to implement namespacing. (And yes, I'm running php 5.3). So my question is, how exactly would I implement namespacing where the front Controller and Model is extended by other controllers and models?
One way to do it would be to manually prepend the namespace assuming your controllers are living in the same space. Take the following example.
public function __construct( $controller, $model )
{
$this->controller = 'Application\Controllers\\' . $controller;
$this->model = 'Application\Models\\' . $model;
}
There may be a more autoload-ish way of doing it but I think this will suffice in most cases. Don't quote me but perhaps you could use Reflection and get the namespace of the called object. However, this may still require a more unique naming convention otherwise the autoloader still wouldn't know if you were calling Controllers\Index.php or Views\Index.php.
Always check php.net... http://php.net/namespace

How to Use Same Models in Different Modules in Zend Framework?

I am working on implementing Zend Framework within an existing project that has a public marketing area, a private members area, an administration site, and a marketing campaign management site. Currently these are poorly organized with the controller scripts for the marketing area and the members area all being under the root of the site and then a separate folder for admin and another folder for the marketing campaign site.
In implementing the Zend Framework, I would like to create be able to split the controllers and views into modules (one for the members area, one for the public marketing area, one for the admin site, and one for the marketing campaign admin site) but I need to be able to point each module to the same model's since all three components work on the same database and on the same business objects.
However, I haven't been able to find any information on how to do this in the documentation. Can anyone help with either a link on how to do this or some simple instructions on how to accomplish it?
What I do is keep common classes in a "library" directory outside of the modules hierarchy. Then set my INCLUDE_PATH to use the "models" directory of the respective module, plus the common "library" directory.
docroot/
index.php
application/
library/ <-- common classes go here
default/
controllers/
models/
views/
members/
controllers/
models/
views/
admin/
controllers/
models/
views/
. . .
In my bootstrap script, I'd add "application/library/" to the INCLUDE_PATH. Then in each controller's init() function, I'd add that module's "models/" directory to the INCLUDE_PATH.
edit: Functions like setControllerDirectory() and setModuleDirectory() don't add the respective models directories to the INCLUDE_PATH. You have to do this yourself in any case. Here's one example of how to do it:
$app = APPLICATION_HOME; // you should define this in your bootstrap
$d = DIRECTORY_SEPARATOR;
$module = $this->_request->getModuleName(); // available after routing
set_include_path(
join(PATH_SEPARATOR,
array(
"$app{$d}library",
"$app{$d}$module{$d}models",
get_include_path()
)
)
);
You could add the "library" to your path in the bootstrap, but you can't add the "models" directory for the correct module in the bootstrap, because the module depends on routing. Some people do this in the init() method of their controllers, and some people write a plugin for the ActionController's preDispatch hook to set the INCLUDE_PATH.
This can also be accomplished through a naming convention to follow Zend_Loader. Keep your model files in the models folder under their module folder. Name them as Module_Models_ModelName and save them in a file name ModelName.php in the models folder for that module. Make sure the application folder is in your include path and assuming you are using Zend_Loader for auto loading, you can then just reference the models by their class name.
This has the advantage of keeping your model code grouped in with the actual module it is for. This keeps the module contained within a single folder structure which helps encourage encapsulation. This will also help in the future if you need to port the module to another project.
I just built this custom Action Helper for the problem you describe:
<?php
class My_Controller_Action_Helper_GetModel extends Zend_Controller_Action_Helper_Abstract
{
/**
* #var Zend_Loader_PluginLoader
*/
protected $_loader;
/**
* Initialize plugin loader for models
*
* #return void
*/
public function __construct()
{
// Get all models across all modules
$front = Zend_Controller_Front::getInstance();
$curModule = $front->getRequest()->getModuleName();
// Get all module names, move default and current module to
// back of the list so their models get precedence
$modules = array_diff(
array_keys($front->getDispatcher()->getControllerDirectory()),
array('default', $curModule)
);
$modules[] = 'default';
if ($curModule != 'default') {
$modules[] = $curModule;
}
// Generate namespaces and paths for plugin loader
$pluginPaths = array();
foreach($modules as $module) {
$pluginPaths[ucwords($module)] = $front->getModuleDirectory($module) . '/models';
}
// Load paths
$this->_loader = new Zend_Loader_PluginLoader($pluginPaths);
}
/**
* Load a model class and return an object instance
*
* #param string $model
* #return object
*/
public function getModel($model)
{
$class = $this->_loader->load($model);
return new $class;
}
/**
* Proxy to getModel()
*
* #param string $model
* #return object
*/
public function direct($model)
{
return $this->getModel($model);
}
}
So in your Bootstrap.php:
Zend_Controller_Action_HelperBroker::addPrefix('My_Controller_Action_Helper');
And in any of your controllers:
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$model = $this->_helper->getModel('SomeModel');
}
}
And this will allow your access to models in any controller across all modules.
I'm having the same problem.
Bill's answer doesn't fit for me - cos i tend to divide my modules, not by 'who is seeing them', but by 'what they do'. E.g a 'forum module' might be managed by both admin and public.
I'm trying to have front end modules, like admin, members , public - but these then use other modules like 'forum/validatepost', 'forum/show users personal info'.
If anyone could shed light on how they protect a back-end module from the public , then that would be handy. I guess ACL may be the key but it still makes me nervous having access controlled by objects as opposed 'file system/.htaccess' etc.
To answer PHPoet's question :
(i) Paths to module's controller directories can be specified by calls to front controller:
e.g see : "12.11.2. Specifying Module Controller Directories" (Zend Framework Docs)
(ii) Paths to views can be set using ViewRenderer (Controller Action Helper)
e.g. see: 'Example 12.12. Choosing a Different View Script' (Zend Framework Docs)
By playing around its possible to alter the default paths to views and controllers, thus freeing up your autoloader to run as normal.
(I have not looked into the way autoloader works, but it would make sense for it to have some mapper system to solve this kind of issue.)
<?php
return array(
'modules' => array(
'Application',
'DoctrineModule',
'DoctrineORMModule',
'Merchant',
),
'module_listener_options' => array(
'config_glob_paths' => array(
'config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'./module',
'../vendor',
// 'here we can load module'
'comomonmodule'
),
),
);

Categories