i am using a modular architecture with Zend framework, and i want to use the module specific database configuration such that each module's model employs its own database configuration.
This is my sample application architecture:
application
modules
admin
controllers
models
DbTable
Users.php
views
configs
module.ini
configs
application.ini
From the above architecture, i have the module "admin" and i have specified its database configuration in admin/configs/module.ini like:
resources.db.adapter = "pdo_mysql"
resources.db.params.host = "localhost"
resources.db.params.username = "*****"
resources.db.params.password = "*****"
resources.db.params.dbname = "********"
Now I want to use this configuration in the models of "admin" module.
FYI, Iam instantiating the model in the "admin" module's controller like:
$admin = new Admin_Models_DbTable_Users();
$admin_users = $admin->fetchUsers();
And in the "admin" module's model, i am executing the queries like:
public function fetchUsers()
{
$sql = "select * from users";
return $this->getAdapter()->fetchAll($sql, array(), Zend_Db::FETCH_OBJ);
}
How can i load the database configuration in admin/configs/module.ini in my "admin" module's database adapter such that it uses that configuration? Do i need to use Zend Registry or set any options in admin module's bootstrap file ?
You should use multidb resource plugin. In short, in you application.ini you should setup all your databases:
resources.multidb.employees.adapter = PDO_MYSQL
resources.multidb.employees.host = localhost
resources.multidb.employees.username = zend
resources.multidb.employees.password = zend
resources.multidb.employees.dbname = zend
resources.multidb.employees.default = true
resources.multidb.employees1.adapter = "PDO_SQLITE"
resources.multidb.employees1.dbname = APPLICATION_PATH "/../data/db/employeesdb.db"
Then in you Bootstrap.php you should save them into registry:
public function run()
{
$resource = $this->getPluginResource('multidb');
Zend_Registry::set('news', $resource->getDb('employees1'));
Zend_Registry::set('employees', $resource->getDb('employees'));
parent::run();
}
And at last in your db model class you should write:
/**
* Db name.
*
* #var string
*/
protected $_use_adapter = 'employees1';
and (this part can be put to some abstract class which extends Zend_Db_Table_Abstract and will be parent for all your db models classes):
public function __construct($config = null)
{
if(isset($this->_use_adapter)){
$config = $this->_use_adapter;
}
return parent::__construct($config);
}
i am following a different approach, as suggested by one of my friends:
In the module's bootstrap file, i am setting the module's db adapter into the Registry, and in the module's model file, i am getting the module's db adapter from the registry using a custom method: getAdapter()
Code in ....application/modules/admin/Bootstrap.php
protected function _initAutoload()
{
$configuration = new Zend_Config_Ini(
APPLICATION_PATH . '/modules/admin/configs/module.ini',
'production'
);
$params = $configuration->resources->db->params->toArray();
$DB = new Zend_Db_Adapter_Pdo_Mysql($params);
Zend_Registry::set('DB',$DB);
}
Code in ...application/modules/admin/DbTable/Users.php
public function getAdapter()
{
$registry = Zend_Registry::getInstance();
return $registry['DB'];
}
So now the $this->getAdapter() in admin module's fetchUsers() method will have the db adapter of "admin" module. :)
FYI: my friend also wanted to do this. He set a default adapter in the application.ini. Then in each zend_db model where he wanted to use another database, he changed the schema configuration. He said it was the easiest. Then his db model already had the authentication and host, and he just overwrote the database and table. Definitely consider this option; sorry I don't have any links or examples.
Related
I am migrating a project from Zend framework 1.4 to 2.4, I have a class in "vendor/custom/classes/User.php"
<?php
namespace Classes;
use Zend\Db\TableGateway\TableGateway;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Db\Adapter\Adapter;
class User
{
public function getItemById($id)
{
//$config = $this->getServiceLocator()->get('config');
//This only work in controller
$configs = array();
$adapter = new Adapter($configs);
$projectTable = new TableGateway('project', $adapter);
$rowset = $projectTable->select(array('type' => 'PHP'));
echo 'Projects of type PHP: ';
foreach ($rowset as $projectRow) {
echo $projectRow['name'] . PHP_EOL;
}
}
}
?>
I need to load merged configurations in my files in "config/autoload" , global.php and local.php. $config = $this->getServiceLocator()->get('config'); Can someone guide me how can I get these configurations from a custom class. Basically I am trying to do is writing set of classes like User, Project, Customer outside of Models and use them commonly in all modules like CMS, Admin Panel, Web site. Appreciate your guidance.
An approach could be using a factory.
You create a class UserFactory implementing Zend\ServiceManager\FactoryInterface. This class will have a method createService with a $serviceLocator parameter. You use this service locator to retrieve your dependencies and pass them to your User class.
In your User class you need to use a controller that accepts as parameters the dependencies that you need to pass to it
Since there is no direct way to access those configurations. I have wrote constants with DB access information in the local and global php files in config/autoload and used it in my class.
class DBManager
{
protected $adapter ;
protected $connection ;
/**
* Constructor
*/
public function __construct()
{
$configs = array(
'hostname' => DB_SERVER_NAME,
'driver' => 'Pdo_Mysql',
'database' => DB_DATABASE_NAME,
'username' => DB_USER_NAME,
'password' => DB_PASSWORD ,
);
$this->adapter = new Adapter($configs);
}
}
I'm trying to change my database connection depending on who is trying to log in to my page.
What I need is a way to save the correct db name so its accessible by all my controllers.
Using a session would work, but I doubt its safe and/or good practice.
If I could set a variable in the AppController from my AccountsController that would be perfect. but basically any way that enables me to share a variable between all controllers.
In my AccountsController I query a standard database for the correct name. then I use configure::write('CompanyDB', $myDbVar). this work fine for this controller, but I cant use configure::read('CompanyDB') in any other controllers.
In my AppModel i have a construct fucntion that sets the db connection depending on the value inside configure::read('campanyDB') as mentioned before, I need to use configure::write('CompanyDB',$myDbVar) in all my controllers for this to work.
In my Account Model I set the $specific=true. this tells the AppModel that it should use the construct and change the db connection.
class AccountsController extends AppController {
public $helpers = array('Html', 'Form','Js');
public $components = array('RequestHandler');
var $uses = array('User', 'Company');
public $name = 'Accounts';
public $myDbVar='coolbeans';
public function beforeFilter() {
parent::beforeFilter();
Configure::write( 'companyDB',$this->myDbVar);
}
}
class AppModel extends Model {
var $specific = false;
function __construct($id = false, $table = null, $ds = null) {
if ($this->specific) {
// Get saved company/database name
$dbName = Configure::read('companyDB');
// Get common company-specific config (default settings in database.php)
$config = ConnectionManager::getDataSource('companySpecific')->config;
// Set correct database name
$config['database'] = $dbName;
// Add new config to registry
ConnectionManager::drop('companySpecific');
ConnectionManager::create('companySpecific', $config);
// Point model to new config
$this->useDbConfig = 'companySpecific';
}
parent::__construct($id, $table, $ds);
}
}
class Account extends AppModel {
public $primaryKey = '_id';
//var $useDbConfig = 'mongo';
var $specific = true;
.....
}
Probably best bet would be to go with a Configuration file:
Reading and Writing Configuration Files:
http://book.cakephp.org/2.0/en/development/configuration.html#reading-and-writing-configuration-files
The basic idea is, you create a file in your Config/ directory with settings for your app, then, in the bootstrap, you load that config file, which makes any of those variables available anywhere in the app.
Example file: Config/dbconnections.php
<?php
$config = array(
'MyAppDBs' => array(
'company1' => 'connectionName',
'company2' => 'differentConnectionName
)
);
In your bootstrap file:
Configure::load('dbconnections');
Anywhere in your App:
$whatever = Configure::read('MyAppDBs.companyDB');
I think if you do this
configure::write('CompanyDB', $myDbVar);
in your appController then you can access it in any controller using
configure::write('CompanyDB',$myDbVar);
since all controller inherits appController.
I've been working with Doctrine_Record classes that autoload just fine for a while; but after some reading, I've decided I would like to implement both Doctrine_Records as well as Custom Table Classes.
So I added this to my bootstrap
$manager->setAttribute(
Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES,
true
);
Which has made the Custom table classes work just fine... but it breaks autoloading Records!
How to make both autoload?
I.E. new User.php gets my User Doctrine_Record class and Doctrine_Core::getTable('User') gets my Custom UserTable class.
Here's how it looked (working) before I tried implementing Custom Tables:
public function _initDoctrine() {
require_once 'Doctrine.php';
/*
* Autoload Doctrine Library and connect
* use appconfig.ini doctrine.dsn
*/
$this ->getApplication()
->getAutoloader()
->pushAutoloader(array(
'Doctrine',
'autoload'),
'Doctrine');
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(
Doctrine::ATTR_AUTO_ACCESSOR_OVERRIDE,
true
);
$manager->setAttribute(
Doctrine::ATTR_MODEL_LOADING,
Doctrine::MODEL_LOADING_CONSERVATIVE
);
// try these custom tables out!
// $manager->setAttribute( Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, true );
$config = $this->getOption('doctrine');
$conn = Doctrine_Manager::connection($config['dsn'], 'doctrine');
return $conn;
// can call flush on the connection to save any unsaved records
}
Thanks
edit:
Let me clarify.
Not just custom classes.. I already use custom classes which extend Doctrine_Record.
class Model_User extends Doctrine_Record {}
$foo = new Model_User;
Much of my application currently works around this and will not be changing in that respect.
However, I would like to ALSO use Custom Tables
class UserTable extends Doctrine_Table {}
$bar = Doctrine_Core::getTable('User');
But, as soon as I enable this (custom table classes) feature to call classes of Doctrine_Table utilising the Table suffix. Any Doctrine_Record classes I've previously extended and called directly, stops working! I want to make use of both!
I don't really understand your problem about your custom classes but in any case here is my bootstrap for Doctrine 1.2.4 in ZF, using mysql and UTF-8. It doesn't need a require() and load all my models flawlessly.
protected function _initDoctrine()
{
$this->getApplication()->getAutoloader()
->pushAutoloader(array('Doctrine', 'autoload'));
spl_autoload_register(array('Doctrine', 'modelsAutoload'));
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
$manager->setAttribute (
Doctrine::ATTR_MODEL_LOADING,
Doctrine::MODEL_LOADING_CONSERVATIVE
);
$manager->setAttribute(Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES, true);
$dsn = $this->getOption('dsn');
$conn = Doctrine_Manager::connection($dsn, 'doctrine');
$conn->setAttribute(Doctrine::ATTR_USE_NATIVE_ENUM, true);
$conn->setCollate('utf8_unicode_ci');
$conn->setCharset('utf8');
$conn->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true );
Doctrine::loadModels(APPLICATION_PATH . '/models');
return $manager;
}
Doctrine models are stored in "application/models"
Then in my application/configs/application.ini :
autoloadernamespaces[] = "Doctrine"
dsn = "mysql://your_login:your_pass#server_ip/database"
I found the problem!
You must make sure every x.php Doctrine_Record class has an associated xTable.php Doctrine_Table class or the record loading will break!
I've recently started working with Zend Framework and I've absolutely fallen in love with it and I've even decommissioned my own framework in favour of it.
But I'm missing something that is probably so painfully obvious you'll chuckle a little bit.
I have a login system and in every controller I have to put the check for the login status, I had a look at accessing the Zend Session Storage in the Bootstrap but I'm on bit of a deadline and can't afford to waste time, is there a better way to check IE in the bootstrap? instead of repeating 20+ lines of code and functionality in every controller.
Thanks in advance!
You can use a controller plugin for this. See: http://framework.zend.com/manual/en/zend.controller.plugins.html
class Your_Plugin_Login extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $request)
{
if (!Zend_Auth::getInstance()->hasIdentity()) {
// send the user to the login page
$request->setControllerName('login')
->setActionName('login');
}
}
}
replace the controller/action names with whatever is appropriate for your login page, and 'Your' with your application's namespace.
You then register the plugin with the Front controller either in application.ini or in your bootstrap with Zend_Controller_Front::getInstance()->registerPlugin(new Your_Plugin_Login());.
Edit: If you want to put the user details in the view as well, you can do:
$layout = Zend_Layout::getMvcInstance();
$view = $layout->getView();
$view->user = Zend_Auth::getIdentity();
You can write your own controller that inherits from Zend Controller (call it e.g. DaveMac_Controller - you can define the prefix in the app's config file so that the app can autoload it (and you need to be careful with which directory you save your class in)). In this class's construct function run the check for authentication. Then change all your page's controllers to inherit from DaveMac_Controller rather than the default zend one.
If I was at home I could copy and paste some code as an example, but at work right now so hopefully the above outline is enough.
*edit Good ol' dropbox :)
application.ini
includePaths.library = APPLICATION_PATH "/../library"
autoloaderNamespaces[] = "DaveMac_"
/../library/DaveMac/Controller/Action.php
<?php
class DaveMac_Controller_Action extends Zend_Controller_Action {
protected $acl;
protected $user;
protected $userRole;
public function init() {
//retrieve and store user details
$auth = Zend_Auth::getInstance();
if($auth->hasIdentity()){
$user = $auth->getIdentity();
$this->user = $user;
$this->view->user = $user;
$this->userRole = $user->role;
} else {
$this->userRole = "guest";
}
//Initialise access control list
$this->acl = new DaveMac_Acl();
}
protected function checkAuth($pageLevel, $redirect = "/") {
if($this->user) {
if(!$this->acl->isAllowed($this->userRole, $pageLevel)) {
$this->_redirect($redirect);
}
} else if ($pageLevel != DaveMac_Resources::PUBLIC_ONLY_PAGE) {
$this->_redirect('/login/returnurl/' . str_replace('/','-',$this->getRequest()->getRequestUri()));
}
}
}
You will probably already have your own functioms fdor checking authentication, but I thought I'd leave mine in anyway
What I want to do:
Create a number of modules with the forms folder inside them.
What I did:
Create a bootstrapper inside the module and added an _initAutoload function with the specific module name as namespace.
For instance, an admin module with the following bootstrapper:
class Admin_Bootstrap extends Zend_Application_Module_Bootstrap
{
protected function _initAutoload()
{
$autoloader = new Zend_Application_Module_Autoloader(array(
'namespace' => 'Admin_',
'basePath' => dirname(__FILE__),
));
return $autoloader;
}
}
My question is:
Is this the correct way of doing what I want? - I tried it without having the admin bootstrapper, but it couldn't find my form, until I added the bootstrapper.
Cheers
Chris
The autoloader is automatically set up for each module bootstrap. You don't need to configure it manually.
class Admin_Bootstrap extends Zend_Application_Module_Bootstrap {}
is all you need.
Then put your forms in /application/modules/admin/forms/.
Admin_Form_Myform extends Zend_Form {...}
For your custom resources, customize resourceAutoloader:
class Admin_Bootstrap extends Zend_Application_Module_Bootstrap
{
public function _initAuloload()
{
$resourceLoader = $this->_resourceAuloloader;
// var_dump($resourceLoader);
}
}
Remember to add also in your apllication.ini
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"
resources.modules = ""