Zend Framework 2 - how to unit test own session service? - php

I have a problem with unit testing of my own SessionManager service. I don't have errors in unit tests but session isn't created in database and i can't write to storage. Here is my code:
SessionManagerFactory:
namespace Admin\Service;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\Session\SaveHandler\DbTableGatewayOptions as SessionDbSavehandlerOptions;
use Zend\Session\SaveHandler\DbTableGateway;
use Zend\Session\Config\SessionConfig;
use Zend\Session\SessionManager;
use Zend\Db\TableGateway\TableGateway;
class SessionManagerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
return $this;
}
public function setUp(ServiceManager $serviceManager)
{
$sessionOptions = new SessionDbSavehandlerOptions();
$sessionOptions->setDataColumn('data')
->setIdColumn('id')
->setModifiedColumn('modified')
->setLifetimeColumn('lifetime')
->setNameColumn('name');
$dbAdapter = $serviceManager->get('Zend\Db\Adapter\Adapter');
$sessionTableGateway = new TableGateway('zf2_sessions', $dbAdapter);
$sessionGateway = new DbTableGateway($sessionTableGateway, $sessionOptions);
$config = $serviceManager->get('Configuration');
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config['session']);
$sessionManager = new SessionManager($sessionConfig);
$sessionManager->setSaveHandler($sessionGateway);
return $sessionManager;
}
}
GetServiceConfig() method from Module.php in Admin namespace:
public function getServiceConfig()
{
return array(
'factories' => array(
'Zend\Authentication\Storage\Session' => function($sm) {
return new StorageSession();
},
'AuthService' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$authAdapter = new AuthAdapter($dbAdapter, 'zf2_users', 'email', 'password');
$authService = new AuthenticationService();
$authService->setAdapter($authAdapter);
$authService->setStorage($sm->get('Zend\Authentication\Storage\Session'));
return $authService;
},
'SessionManager' => function($serviceManager){
$sessionManager = new SessionManagerFactory();
return $sessionManager->setUp($serviceManager);
}
)
);
}
And setUp() mehod from unit test file:
protected function setUp()
{
$bootstrap = \Zend\Mvc\Application::init(include 'config/app.config.php');
$this->controller = new SignController;
$this->request = new Request;
$this->routeMatch = new RouteMatch(array('controller' => 'sign'));
$this->event = $bootstrap->getMvcEvent();
// Below line should start session and storage it in Database.
$bootstrap->getServiceManager()->get('SessionManager')->start();
// And this line should add test variable to default namespace of session, but doesn't - blow line is only for quick test. I will write method for test write to storage.
Container::getDefaultManager()->test = 12;
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setEventManager($bootstrap->getEventManager());
$this->controller->setServiceLocator($bootstrap->getServiceManager());
}
How to test this service and why session aren't created?

I think you've misunderstood the Factory pattern. Your factory should look like the following. The separate setUp method is NEVER called anywhere as far as I can tell. You don't call it manually anywhere.
class SessionManagerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$sessionOptions = new SessionDbSavehandlerOptions();
$sessionOptions->setDataColumn('data')
->setIdColumn('id')
->setModifiedColumn('modified')
->setLifetimeColumn('lifetime')
->setNameColumn('name');
$dbAdapter = $serviceManager->get('Zend\Db\Adapter\Adapter');
$sessionTableGateway = new TableGateway('zf2_sessions', $dbAdapter);
$sessionGateway = new DbTableGateway($sessionTableGateway, $sessionOptions);
$config = $serviceManager->get('Configuration');
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config['session']);
$sessionManager = new SessionManager($sessionConfig);
$sessionManager->setSaveHandler($sessionGateway);
return $sessionManager;
}
}
All the following code works for me. I think you are missing some other things but the above should fix it. Look for the SEE ME comments below. Also, add an onBootstrap method to your Module.php like I have below and make sure to call the $sessionManager = $serviceManager->get( 'SessionManager' ); in your case so that your SessionFactory is actually called. You already call it in your setup() function in the unit test but if you call it in the module you won't have to manually call it yourself.
In application.config I have this
'session' => array(
'name' => 'PHPCUSTOM_SESSID',
'cookie_lifetime' => 300, //1209600, //the time cookies will live on user browser
'remember_me_seconds' => 300, //1209600 //the time session will live on server
'gc_maxlifetime' => 300
)
'db' => array(
'driver' => 'Pdo_Sqlite',
'database' => '/tmp/testapplication.db'
),
My session factory is very similar but I have one extra line of code. Look for the comment.
use Zend\ServiceManager\FactoryInterface,
Zend\ServiceManager\ServiceLocatorInterface,
Zend\Session\SessionManager,
Zend\Session\Config\SessionConfig,
Zend\Session\SaveHandler\DbTableGateway as SaveHandler,
Zend\Session\SaveHandler\DbTableGatewayOptions as SaveHandlerOptions,
Zend\Db\Adapter\Adapter,
Zend\Db\TableGateway\TableGateway;
class SessionFactory
implements FactoryInterface
{
public function createService( ServiceLocatorInterface $sm )
{
$config = $sm->has( 'Config' ) ? $sm->get( 'Config' ) : array( );
$config = isset( $config[ 'session' ] ) ? $config[ 'session' ] : array( );
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions( $config );
$dbAdapter = $sm->get( '\Zend\Db\Adapter\Adapter' );
$sessionTableGateway = new TableGateway( 'sessions', $dbAdapter );
$saveHandler = new SaveHandler( $sessionTableGateway, new SaveHandlerOptions() );
$manager = new SessionManager();
/******************************************/
/* SEE ME : I DON'T SEE THE LINE BELOW IN YOUR FACTORY. It probably doesn't matter though.
/******************************************/
$manager->setConfig( $sessionConfig );
$manager->setSaveHandler( $saveHandler );
return $manager;
}
In one of my modules I have the following
public function onBootstrap( EventInterface $e )
{
// You may not need to do this if you're doing it elsewhere in your
// application
/* #var $eventManager \Zend\EventManager\EventManager */
/* #var $e \Zend\Mvc\MvcEvent */
$eventManager = $e->getApplication()->getEventManager();
$serviceManager = $e->getApplication()->getServiceManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach( $eventManager );
try
{
//try to connect to the database and start the session
/* #var $sessionManager SessionManager */
$sessionManager = $serviceManager->get( 'Session' );
/******************************************/
/* SEE ME : Make sure to start the session
/******************************************/
$sessionManager->start();
}
catch( \Exception $exception )
{
//if we couldn't connect to the session then we trigger the
//error event
$e->setError( Application::ERROR_EXCEPTION )
->setParam( 'exception', $exception );
$eventManager->trigger( MvcEvent::EVENT_DISPATCH_ERROR, $e );
}
}
}
This is my getServiceConfigMethod
public function getServiceConfig()
{
return array(
'factories' => array(
'Session' => '\My\Mvc\Service\SessionFactory',
'\Zend\Db\Adapter\Adapter' => '\Zend\Db\Adapter\AdapterServiceFactory'
)
);
}
I'm using sqllite at the moment so the table has to exist in your sqllite file already.
If you're using mysql, it should exist in that database too and you should change your db settings in the application.config.php file.

Related

getServiceLocator returning Null under PHPUnittests

I'm trying to test a simple controller that authenticates a user using the LdapAdapter and using the 'ldap' array from the configuration of the Application, but phpunit is returning the following error:
Fatal error: Uncaught Error: Call to a member function get() on null in /var/www/html/app/module/Auth/src/Auth/Controller/AuthController.php:53
Stack trace:
#0 /var/www/html/app/module/Auth/test/AuthTest/Controller/AuthControllerTest.php(37): Auth\Controller\AuthController->authenticate('myuser', 'mypassword')
#1 [internal function]: AuthTest\Controller\AlbumControllerTest->testLoginAction()
#2 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(863): ReflectionMethod->invokeArgs(Object(AuthTest\Controller\AlbumControllerTest), Array)
#3 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(741): PHPUnit_Framework_TestCase->runTest()
#4 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestResult.php(608): PHPUnit_Framework_TestCase->runBare()
#5 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestCase.php(697): PHPUnit_Framework_TestResult->run(Object(AuthTest\Controller\AlbumControllerTest))
#6 /var/www/html/vendor/phpunit/phpunit/src/Framework/TestSuite.php(733): PHPUnit_Framework_TestCase- in /var/www/html/app/module/Auth/src/Auth/Controller/AuthController.php on line 53
My Controller is the following:
<?php
namespace Auth\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Authentication\Adapter\Ldap as AuthAdapter;
use Zend\Authentication\AuthenticationService;
use Zend\Authentication\Result;
use Auth\Form\AuthForm;
use Auth\Model\Auth;
class AuthController extends AbstractActionController
{
public function loginAction()
{
$form = new AuthForm();
$request = $this->getRequest();
if ($request->isPost()) {
$auth = new Auth();
$form->setInputFilter($auth->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()){
$auth->exchangeArray($form->getData());
$values = $form->getData();
$result = $this->authenticate($values['username'], $values['password']);
switch($result->getCode()) {
case Result::SUCCESS:
return $this->redirect()->toRoute('home');
break;
case Result::FAILURE:
break;
}
}
}
return array('form' => $form);
}
public function authenticate($username, $password){
$options = $this->getServiceLocator()->get('Config');
$authAdapter = new AuthAdapter($options['ldap'],
'username',
'password');
$authAdapter
->setIdentity($username)
->setCredential($password);
$auth = new AuthenticationService();
$result = $auth->authenticate($authAdapter);
return $result;
}
private function debug($var){
echo '<pre>';
var_dump($var);
echo '</pre>';
exit();
}
}
The TestCase:
namespace AuthTest\Controller;
use Zend\Test\PHPUnit\Controller\AbstractHttpControllerTestCase;
use Zend\Authentication\AuthenticationService;
use Auth\Controller\AuthController;
class AuthControllerTest extends AbstractHttpControllerTestCase
{
protected $traceError = true;
public function setUp()
{
$this->setApplicationConfig(
include '/var/www/html/app/config/application.config.php'
);
parent::setUp();
}
public function testLoginAction()
{
#Basic Access to the page
$this->dispatch('/login');
$this->assertResponseStatusCode(200);
$data = array(
'identity' => 'myuser',
'credential' => 'mypassword',
);
$auth = new AuthController();
$auth->authenticate($data['identity'], $data['credential']);
$identity = new AuthenticationService();
$this->assertEquals($data['identity'], $identity->getIdentity());
}
}
PHPUnittest's BootStrap:
<?php
namespace AuthTest;
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use RuntimeException;
error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);
/**
* Test bootstrap, for setting up autoloading
*/
class Bootstrap
{
protected static $serviceManager;
public static function init()
{
$zf2ModulePaths = array(dirname(dirname(__DIR__)));
if (($path = static::findParentPath('vendor'))) {
$zf2ModulePaths[] = $path;
}
if (($path = static::findParentPath('module')) !== $zf2ModulePaths[0]) {
$zf2ModulePaths[] = $path;
}
static::initAutoloader();
// use ModuleManager to load this module and it's dependencies
$config = array(
'module_listener_options' => array(
'module_paths' => $zf2ModulePaths,
),
'modules' => array(
'Auth'
)
);
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
}
public static function chroot()
{
$rootPath = dirname(static::findParentPath('module'));
chdir($rootPath);
}
public static function getServiceManager()
{
return static::$serviceManager;
}
protected static function initAutoloader()
{
$vendorPath = static::findParentPath('vendor');
if (file_exists($vendorPath.'/autoload.php')) {
include $vendorPath.'/autoload.php';
}
if (! class_exists('Zend\Loader\AutoloaderFactory')) {
throw new RuntimeException(
'Unable to load ZF2. Run `php composer.phar install`'
);
}
AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true,
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__,
),
),
));
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) {
return false;
}
$previousDir = $dir;
}
return $dir . '/' . $path;
}
}
Bootstrap::init();
Bootstrap::chroot();
In the functional tests it works as expected, but on php unittests the error occurs in the line 53 '$options = $this->getServiceLocator()->get('Config');'.
So, how can use or set ServiceLocator to work with phpunittests?
After so much struggling on this doubt and other tests which a controller that uses an ServiceLocator, I figure that the correct way to test a controllers Action is to Mock the object or/and his methods.
I don't feel totally comfortable with this solution, but for now, this is what it is.
An other feel is that mocking an behaviour of the code is something like that breaks the DRY principle: instead of use the real code I just create a mock that half behaves like it :S
Anyway, the following code does the job for me:
$authControllerMock = $this->getMockBuilder('Auth\Controller\AuthController')
->disableOriginalConstructor()
->getMock();
$authControllerMock->expects($this->any())
->method('authenticate')
->will($this->returnValue(true);
$serviceManager = $this->getApplicationServiceLocator();
$serviceManager->setAllowOverride(true);
$serviceManager->setService('Auth\Controller\Auth', $authControllerMock);
$this->dispatch('/usermgr');
self::assertMatchedRouteName('usermgr');
self::assertControllerClass('AuthController');
self::assertControllerName('auth\controller\auth');
self::assertResponseStatusCode('200');

How to mock ServiceLocator ZF2?

Here is a Factory :
namespace Maintenance\Factory\View\Helper;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Maintenance\View\Helper\SousMenuContrat;
class SousMenuContratFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$realServiceLocator = $serviceLocator->getServiceLocator();
$maiContratService = $realServiceLocator->get(
'Maintenance\Service\Model\FMaiContratService'
);
return new SousMenuContrat(
$maiContratService
);
}
}
I have to write some PHPUnit tests, I began to do so :
public function testCreateService()
{
$this->mockDriver = $this->getMock('Zend\Db\Adapter\Driver\DriverInterface');
$this->mockConnection = $this->getMock('Zend\Db\Adapter\Driver\ConnectionInterface');
$this->mockDriver->expects($this->any())->method('checkEnvironment')->will($this->returnValue(true));
$this->mockDriver->expects($this->any())->method('getConnection')->will($this->returnValue($this->mockConnection));
$this->mockPlatform = $this->getMock('Zend\Db\Adapter\Platform\PlatformInterface');
$this->mockStatement = $this->getMock('Zend\Db\Adapter\Driver\StatementInterface');
$this->mockDriver->expects($this->any())->method('createStatement')->will($this->returnValue($this->mockStatement));
$this->adapter = new Adapter($this->mockDriver, $this->mockPlatform);
$this->sql = new Sql($this->adapter);
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway', array(), array(), '', false);
$maiContratTable = $this->getMockBuilder('Maintenance\Model\BDD\FMaiContratTable')
->setMethods(array())
->setConstructorArgs(array($mockTableGateway, $this->adapter, $this->sql))
->getMock();
$smMock = $this->getMockBuilder('Zend\ServiceManager\ServiceManager')
->setMethods(array('get'))
->getMock();
$smMock->expects($this->at(0))
->method('get')
->with('Maintenance\Service\Model\FMaiContratService')
->will($this->returnValue(new FMaiContratService($maiContratTable)));
$factory = new SousMenuContratFactory();
$runner = $factory->createService($smMock);
}
But I got some problems. This tells me :
Call to undefined method Mock_ServiceManager_3ed93deb::getServiceLocator()
What have I misunderstood ?
Thanks !
In your factory you call:
$realServiceLocator = $serviceLocator->getServiceLocator();
But you defined:
$smMock->expects($this->at(0))
->method('get')
The ServiceLocator passed to your factory usually does not have the method getServiceLocator because it already is the service locator. (Edit: Scratch that, PluginManagers do!) Instead use:
public function createService(ServiceLocatorInterface $serviceLocator)
{
$maiContratService = $serviceLocator->get(
'Maintenance\Service\Model\FMaiContratService'
);
return new SousMenuContrat(
$maiContratService
);
}
Edit: Plugin factories are another thing, here's the test code:
public function testCreateService()
{
$maiContractServiceMock = $this->getMockBuilder('Maintenance\Service\Model\FMaiContratService')
->disableOriginalConstructor()
->getMock();
// if you do something with FMaiContratService in the constructor of SousMenuContrat,
// mock more methods here
$smMock = $this->getMockBuilder('Zend\ServiceManager\ServiceManager')
->setMethods(array('get'))
->getMock();
$smMock->expects($this->at(0))
->method('get')
->with('Maintenance\Service\Model\FMaiContratService')
->will($this->returnValue($maiContractServiceMock));
$hpmMock = $this->getMockBuilder('Zend\View\HelperPluginManager')
->setMethods(array('getServiceLocator'))
->getMock();
$hpmMock->expects($this->any())
->method('getServiceLocator')
->will($this->returnValue($smMock));
$factory = new SousMenuContratFactory();
$runner = $factory->createService($hpmMock);
}
In this case, you need a mock of the plugin manager, returning another service locator if getServiceLocator is called. Désolé!

Getting Statement couldn't be produced with sql in zend2?

this is my function in my model:
public function user_login($getData,$session_id){ // manage the user's login
$email = $getData['login_email'];
$password = $getData['login_password'];
$select = $this->adapter->query("select count(*) as counter from users where email = '$email' and password = '".md5($password)."'");
$results = $select->execute();
if ($results->current()['counter'] == 1 ){
$update = $this->adapter->query("update users set session_id = '".$session_id."' where email = '".$email."'");
$update_session = $update->execute();
return 1;
}else{
return 0;
}
}
When I executed the count query it works and im also using this method to get values from queries or to insert data in db. but now i want to do a simple update, which does not work for some reason. I don't know why ? why i'm getting this message ?
UPDATE: this is the content of the Module.php file from my module name folder:
namespace Application;
use Zend\Mvc\ModuleRouteListener;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getServiceConfig() {
return array(
'factories' => array(
'Application\Model\UsersTable' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new Model\UsersTable($dbAdapter);
return $table;
},
),
);
}
}
Had the same issue, found solution here.
Probably unbuffered query?
$statement = $adapter->query("SELECT ...");
$result = $statement->execute();
$result->buffer();
// validate your result

Cannot attach Events in module init()-method

I read about some of the best practices for ZF2. There, it was explained to attach the events from MVC in the init()-Method of the module's Module class:
class Module {
public function getAutoloaderConfig() {
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
);
}
public function init(ModuleManager $moduleManager) {
echo 'init<br>';
$em = $moduleManager->getEventManager();
$em->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
$em->attach(MvcEvent::EVENT_ROUTE, array($this, 'onRoute'));
}
public function onDispatch(MvcEvent $e){
echo 'onDispatch<br>';
}
...
It results in getting no error, nice. But the event is not caught...
Any ideas? I tried the SharedManager too, but it only worked for the EVENT_DISPATCH ...
Unless for specific cases, it's better to register your events in onBootstrap.
init is for "early events".
I found a link that is quite clear : http://samsonasik.wordpress.com/2013/03/30/zend-framework-2-getting-closer-with-eventmanager/
You can find the order of defaults MVC events in Zend\ModuleManager\Listener\DefaultListenerAggregate::attch :
public function attach(EventManagerInterface $events)
{
$options = $this->getOptions();
$configListener = $this->getConfigListener();
$locatorRegistrationListener = new LocatorRegistrationListener($options);
// High priority, we assume module autoloading (for FooNamespace\Module classes) should be available before anything else
$this->listeners[] = $events->attach(new ModuleLoaderListener($options));
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE_RESOLVE, new ModuleResolverListener);
// High priority, because most other loadModule listeners will assume the module's classes are available via autoloading
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new AutoloaderListener($options), 9000);
if ($options->getCheckDependencies()) {
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new ModuleDependencyCheckerListener, 8000);
}
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new InitTrigger($options));
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new OnBootstrapListener($options));
$this->listeners[] = $events->attach($locatorRegistrationListener);
$this->listeners[] = $events->attach($configListener);
return $this;
}
You'll be better using the shared manager. The example bellow is for disabling layout when we got a xmlHttpRequest and the priority -95 is a key point to make things work.
public function onBootstrap(MvcEvent $e) {
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
// Hybrid view for ajax calls (disable layout for xmlHttpRequests)
$eventManager->getSharedManager()->attach('Zend\Mvc\Controller\AbstractController', MvcEvent::EVENT_DISPATCH, function(MvcEvent $event){
/**
* #var Request $request
*/
$request = $event->getRequest();
$viewModel = $event->getResult();
if($request->isXmlHttpRequest()) {
$viewModel->setTerminal(true);
}
return $viewModel;
}, -95);
}
See http://akrabat.com/zend-framework-2/module-specific-bootstrapping-in-zf2/
Only found this solution: attach listeners in onBootstrap()-Method in the Module.php class:
...
public function onBootstrap(MvcEvent $e){
echo 'onBootstrap<br>';
$em = $e->getApplication()->getEventManager();
$em->attach(MvcEvent::EVENT_DISPATCH, array($this, 'onDispatch'));
$em->attach(MvcEvent::EVENT_ROUTE, array($this, 'onRoute'));
}
...

Zend_Log_Writer_Db - Call to undefined method Zend_Config::insert()

I am running php 5.2.12 and Zend Framework 5.0.2
In my Bootstrap.php, I initialize our database connection, and initialize our logger... placing both in the registry.
However, when I try to log info in the IndexController.php, it gives the following message:
"*Fatal error: Call to undefined method Zend_Config::insert() in /usr/local/zendsvr/share/ZendFramework/library/Zend/Log/Writer/Db.php on line 137*"
At the bottom of this post, you will find the Zend Framework's class file, db.php , and the _write function being called.
I believe the problem is that I am getting the database connection options from my application.ini... and there is no insert() function defined in my application.ini for the database. But I dont really know how to add one to the config, or how I should be doing this.
Bootstrap.php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initConfig()
{
Zend_Registry::set('config', new Zend_Config($this->getOptions()));
}
protected function _initDatabases()
{
$this->bootstrap('multidb');
$resource = $this->getPluginResource('multidb');
$databases = Zend_Registry::get('config')->resources->multidb;
foreach ($databases as $name => $adapter)
{
$db_adapter = $resource->getDb($name);
Zend_Registry::set($name, $db_adapter);
}
}
protected function _initLog()
{
$db = Zend_Registry::get('config')->resources->multidb->as400;
$columnMapping = array('ILGID' => 'id', //1 numeric
'ILGLVL' => 'priority', //2 numeric
'ILGDTE' => 'date', //yymmdd
'ILGTME' => 'time', //hhmmss
'ILGPGM' => 'program', //40 alnum
'ILGURL' => 'url', //2100
'ILGUSR' => 'user', //30
'ILGMSG' => 'message'); //1000
$writer = new Zend_Log_Writer_Db($db, 'dwhlib.intralog', $columnMapping);
$logger = new Zend_Log($writer);
$date = new Zend_Date();
date_default_timezone_set('America/Chicago');
$logger->setEventItem('id' , 1);
$logger->setEventItem('date' , $date->get('Ymd'));
$logger->setEventItem('time' , $date->get('Hms'));
$logger->setEventItem('program' , 'testProgramName'); $logger->setEventItem('url' , $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
$logger->setEventItem('user' , gethostbyaddr($_SERVER['REMOTE_ADDR']));
Zend_Registry::set('logger', $logger);
}
}
application.ini
resources.multidb.as400.adapter = "db2"
resources.multidb.as400.host = "i5"
resources.multidb.as400.username = "removedUsername"
resources.multidb.as400.password = "removedPassword"
resources.multidb.as400.dbname = "*LOCAL"
resources.multidb.as400.default = true
IndexController.php
include("/www/zendserver/htdocs/development/application/models/as400.php");
class IndexController extends Zend_Controller_Action
{
public function init()
{
Zend_Registry::get('logger')->info("this is a test message");
}
public function indexAction()
{
// action body
}
}
as400.php
Class default_Model_As400 extends Zend_Db {
public static function ExecuteSelect($sql, $mode = Zend_Db::FETCH_ASSOC, $log = false)
{
$stmt = self::getStmt($sql);
$stmt->setFetchMode($mode);
$stmt->execute();
if($log === true) {
Zend_Registry::get('logger')->info($sql);
}
$rows = $stmt->fetchAll();
return $rows;
}
private static function getStmt($sql){
$db = Zend_Registry::get('config')->resources->multidb->as400;
$abstractAdapter = new Zend_Db_Adapter_Db2($db);
return new Zend_Db_Statement_DB2($abstractAdapter, $sql);
}
public function insert($libAndFile, $arrData){
echo "this was hit!!";
}
}
db.php
class Zend_Log_Writer_Db extends Zend_Log_Writer_Abstract
{
public function __construct($db, $table, $columnMap = null)
{
$this->_db = $db;
$this->_table = $table;
$this->_columnMap = $columnMap;
}
protected function _write($event)
{
if ($this->_db === null) {
require_once 'Zend/Log/Exception.php';
throw new Zend_Log_Exception('Database adapter is null');
}
if ($this->_columnMap === null) {
$dataToInsert = $event;
} else {
$dataToInsert = array();
foreach ($this->_columnMap as $columnName => $fieldKey) {
$dataToInsert[$columnName] = $event[$fieldKey];
}
}
$this->_db->insert($this->_table, $dataToInsert);
}
}
What is happening: you are calling a method called insert() on a Zend_Config instance.
What you want: call a method insert() through a Zend_Db_Adapter.
There is something wrong in your _initLog() bootstrap method:
$db = Zend_Registry::get('config')->resources->multidb->as400;
$writer = new Zend_Log_Writer_Db($db, 'dwhlib.intralog', $columnMapping);
Zend_Log_Writer_Db expects a Zend_Db adapter as a first constructor parameter. To fix this, since you already registered your database adapter in the registry, you should do something like this :
$dbName = Zend_Registry::get('config')->resources->multidb->as400;
$db = Zend_Registry::get($dbName);
$writer = new Zend_Log_Writer_Db($db, 'dwhlib.intralog', $columnMapping);

Categories