Pagination in CakePHP 3.0 - php

I have been playing with the developer preview version of CakePHP 3.0 and I'm stuck trying to get the new ORM working with pagination.
In my PostsController.php I have:
<?php
namespace App\Controller;
use App\Controller\AppController;
class PostsController extends AppController {
public $name = 'Posts';
public $uses = 'Posts';
public $components = ['Paginator'];
public $paginate = [
'fields' => ['Posts.id'],
'limit' => 1,
'order' => [
'Post.id' => 'asc'
]
];
public function index() {
$posts = $this->paginate('Posts');
$this->set('posts', $posts);
}
}
However the paging is working but the data doesn't come back. Apparently this because the data isn't directly returned in the new ORM but an object... Has anyone tried this yet? Knows how to fix the issue to get it working with the paginator?
I've been reading the Migration Guide: http://book.cakephp.org/3.0/en/appendices/orm-migration.html but don't see anything about combining it with the paginator.
Note: I can't debug $posts and show it here because it's about 2000 lines of code containing all sorts of stuff about the ORM. Here's a taster...
object(Cake\ORM\ResultSet) {
[protected] _query => object(Cake\ORM\Query) {
[protected] _table => object(Cake\ORM\Table) {
[protected] _table => 'posts'
[protected] _alias => 'Posts'
[protected] _connection => object(Cake\Database\Connection) {
[protected] _config => array(
'password' => '*****',
'login' => '*****',
'host' => '*****',
'database' => '*****',
'prefix' => '*****',
'persistent' => false,
'encoding' => 'utf8',
'name' => 'default',
'datasource' => object(Cake\Database\Driver\Mysql) {
[protected] _baseConfig => array(
'password' => '*****',
'login' => '*****',
'host' => '*****',
'database' => '*****',
'port' => '*****',
'persistent' => true,
'flags' => array(),
'encoding' => 'utf8',
'timezone' => null,
'init' => array(),
'dsn' => null
)
[protected] _config => array(
'password' => '*****',
'login' => '*****',
'host' => '*****',
'database' => '*****',
'port' => '*****',
'prefix' => '*****',
'persistent' => false,
'encoding' => 'utf8',
'name' => 'default',
'flags' => array(),
'timezone' => null,
'init' => array(),
'dsn' => null
)
[protected] _autoQuoting => false...
So as you can see it's a huge object and presumably the data is somewhere within it.

Apparently this because the data isn't directly returned in the new
ORM but an object...
It's not a bug it's a feature. ;) CakePHP3 returns a ResultSet object as you can see and entity objects for records. You'll have to work with these objects now instead of arrays.
I wounder if you really read the migration guide you've linked because it is all in there:
Cake\ORM\ResultSet - A collection of results that gives powerful tools for manipulating data in aggregate.
Cake\ORM\Entity - Represents a single row result. Makes accessing data and serializing to various formats a snap.
Further down on that page there is even more info about that. Take a look at the ResultSet API. You'll see that it implements Iterator, you can use it like an array:
Controller method:
public function index() {
$this->set('users', $this->Paginator->paginate($this->Users, [
'limit' => 5,
'conditions' => [
'Users.active' => 1
]
]));
}
There is a lot of documentation to read in the doc block of the paginate() method.
View index.ctp:
foreach ($users as $user) {
debug($user);
}
This will show you Entity objects. I'm not pasting the whole long debug output here, just a part of it.
object(Cake\ORM\Entity) {
[protected] _properties => array(
'password' => '*****',
'id' => '52892217-91ec-4e5d-a9f4-1b6cc0a8000a',
'username' => 'burzum',
'slug' => '',
// ...
To get something from the object back just do this:
echo $user->username;
The actual data is in the protected property Entity::$_properties and accessed by __get.

This will be in your controller.
public function index() {
$this->set('users', $this->paginate($this->Users));
$this->set('_serialize', ['users'])
}
This you can put in your action
Paginatore logic

Related

CakePHP 3 fixtures not Importing

I am using phpunit for testing a CakePHP 3 Controller. The problem is that it does not importing the fixtures. It is just importing live DB data. Reading and writing just uses the LIVE DB even though I have defined a test DB and fixtures. Am I doing something wrong below?
My Fixture
class ToursFixture extends TestFixture {
public $connection = 'test';
public $fields = [
'id' => ['type' => 'integer'],
'title' => ['type' => 'string', 'length' => 255, 'null' => false],
'created' => 'datetime',
'modified' => 'datetime'
'_constraints' => [
'primary' => ['type' => 'primary', 'columns' => ['id']]
]
];
public $records = [
[
'title' => 'tour 1',
'created' => '2015-07-24 09:15:48',
'modified' => '2015-07-24 09:15:48',
],
];
}
My Test Class
namespace App\Test\TestCase\Controller;
use Cake\ORM\TableRegistry;
use Cake\TestSuite\IntegrationTestCase;
use Cake\TestSuite\TestCase;
use App\Controller\ToursController;
use Cake\TestSuite\Fixture\TestFixture;
require '../../bootstrap.php';
class ToursControllerTest extends IntegrationTestCase {
public $fixtures = ['app.tours'];
App Config DB
'test' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'persistent' => false,
'host' => 'localhost',

Secondary database connection with Doctrine 2 and ZF2

I am trying to add a extra connection to my doctrine configuration. my orm_default connection works perfectly fine, and now I'm trying to add a new module with its own Doctrine configuration (mostly learning purposes, buts its rather annoying that I can't get it to work).
The module is called Frontpage, and all relevant code is in this one, except for username/password details that resides in local.php...
My error is
Zend\ServiceManager\Exception\ServiceNotCreatedException
An exception was raised while creating "doctrine.entitymanager.orm_hosts"; no instance returned
Also further down the stacktrace (the last exception) which I think is relevant, but don't know how to fix...
Zend\Stdlib\Exception\BadMethodCallException
The option "hydration_cache" does not have a matching setHydrationCache setter method which must be defined
Here is my module config (relevant parts) file:
'doctrine' => [
'connection' => [
'orm_hosts' => [
'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
'params' => [
'host' => '127.0.0.1',
'port' => '3306',
'dbname' => 'hosts',
],
],
],
'entitymanager' => array(
'orm_hosts' => array(
'connection' => 'orm_hosts',
'configuration' => 'orm_hosts'
)
),
'configuration' => array(
'orm_hosts' => array(
'driver' => 'orm_hosts',
'generate_proxies' => true,
'proxy_dir' => 'data/DoctrineORMModule/Proxy',
'proxy_namespace' => 'DoctrineORMModule\Proxy',
'filters' => array(),
'metadata_cache' => 'array',
'query_cache' => 'array',
'result_cache' => 'array',
//'hydration_cache' => 'array',
)
),
'driver' => array(
'orm_hosts' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\DriverChain',
'drivers' => array(
'Common\Entity' => 'Hosts_Driver'
)
),
'Hosts_Driver' => array(
'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
'cache' => 'array',
'paths' => array(
__DIR__ . '/../src/Common/Entity'
)
),
),
'eventmanager' => array(
'orm_hosts' => array()
),
'sql_logger_collector' => array(
'orm_hosts' => array(),
),
'entity_resolver' => array(
'orm_hosts' => array()
),
],
And my Module.php's getServiceConfig():
public function getServiceConfig()
{
return array(
'factories' => array(
'doctrine.connection.orm_hosts' => new Service\DBALConnectionFactory('orm_hosts'),
'doctrine.configuration.orm_hosts' => new Service\ConfigurationFactory('orm_hosts'),
'doctrine.entitymanager.orm_hosts' => new Service\EntityManagerFactory('orm_hosts'),
'doctrine.entity_resolver.orm_hosts' => new Service\EntityResolverFactory('orm_hosts'),
'doctrine.sql_logger_collector.orm_hosts' => new Service\SQLLoggerCollectorFactory('orm_hosts'),
'doctrine.driver.orm_hosts' => new \DoctrineModule\Service\DriverFactory('orm_hosts'),
'doctrine.eventmanager.orm_hosts' => new \DoctrineModule\Service\EventManagerFactory('orm_hosts'),
'DoctrineORMModule\Form\Annotation\AnnotationBuilder\orm_hosts' => function(\Zend\ServiceManager\ServiceLocatorInterface $sl) {
return new \DoctrineORMModule\Form\Annotation\AnnotationBuilder($sl->get('doctrine.entitymanager.orm_hosts'));
},
),
);
}
And here is my getEntityManager() in IndexController that fails
/**
* #return array|EntityManager|object
*/
public function getEntityManager() {
if (NULL === $this->em) {
/** #var \Doctrine\ORM\EntityManager $em */
$em = $this->getServiceLocator()->get('doctrine.entitymanager.orm_hosts');
//$em = $this->getServiceLocator()->get('doctrine')->getManager("orm_hosts");
$this->em = $em;
}
return $this->em;
}
Any help will be gratly appreciated :)
Best regards
Richard
Okay, so I still don't know what is wrong with the above code, but if I remove
'Hydration_cache' => 'array'
In the doctrine configuration from my module config, it actually works! Still, if anyone want's to explain what happens, I would appreciate to know more :)

zf2 dynamic database connection (with parameters)

I'm trying to create a ZF2 application with multiple databases. Based on a user, the database should be dynamically set.
Right now I've the following:
database.local.php
return array(
'db' => array(
'adapters' => array (
'master_db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=master_db;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'USERNAME',
'password' => 'PASSWORD'
),
'tentant_db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=tenant_db;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'USERNAME',
'password' => 'PASSWORD'
),
)
),
'service_manager' => array(
'abstract_factories' => array(
'Zend\Db\Adapter\AdapterAbstractServiceFactory',
)
),
);
For test purposes I've created a form that has a method to fetch some data and put it in a select box. The code to get the database connection is shown in the code below.
MyController.php (in some module)
//... some code
public function someAction(){
$dbAdapter = $this->getServiceLocator()->get('tentant_db');
$form = new AddEolConnectorForm($dbAdapter);
$viewModel = new ViewModel(array(
'form' => $form
));
return $viewModel;
}
//... some more code
My question is, how can I dynamically set the dbname for the tentant_db adapter in my controller (or module)?
Thanks for your help.
The config merge event is one of zend newer event's I believe. It triggers when zend is mergin the config array's which is perfect for the problem you are facing since you can override some array key's dynamically.
public function onMergeConfig(ModuleEvent $e)
{
$configListener = $e->getConfigListener();
$config = $configListener->getMergedConfig(false);
// I'm actually not sure if you have the route match here otherwise you may have to
// use some other method to retrieve the url.
$match = $e->getRouteMatch();
switch ($match) {
case 'first-dependency':
$config['db']['adapter'] => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=master_db;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'USERNAME',
'password' => 'PASSWORD',
),
break;
case 'second-dependency':
$config['db']['adapter'] => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=tenant_db;host=localhost',
'driver_options' => array(
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
),
'username' => 'USERNAME',
'password' => 'PASSWORD',
),
break;
// Pass the changed configuration back to the listener:
$configListener->setMergedConfig($config);
}
Based on the above answer I've created to following:
Module.php
class Module implements AutoloaderProviderInterface
{
public function init(ModuleManager $moduleManager)
{
$events = $moduleManager->getEventManager();
// Registering a listener at default priority, 1, which will trigger
// after the ConfigListener merges config.
$events->attach(ModuleEvent::EVENT_MERGE_CONFIG, array($this, 'onMergeConfig'));
}
public function onMergeConfig(ModuleEvent $e)
{
$db = $this->getTentantDb();
$configListener = $e->getConfigListener();
$config = $configListener->getMergedConfig(false);
$config['db']['adapters']['tenant_db']['dsn'] = 'mysql:dbname='. $db .';host=localhost';
$configListener->setMergedConfig($config);
}
// Some more code
public function getTenantDb(){
$tenant_db = 'tenant_12345'
return $tenant_db;
}
}
I don't know if it is the best solution, but the above code is working. I think the next steps should be to put the code in a generic module or something so I can access it from all my modules.

Get all active DB connections

How to get all active connection when we use multiple databases?
I have tried:
foreach(Yii::app()->getComponents() as $component)
{
if ($component instanceof CDbConnection)
{
die(var_dump($component));
}
}
but it's look like database component not in Yii::app()->getComponents() result.
My db config:
'db'=>array(
'connectionString' => 'pgsql:host=127.0.0.1;port=yyyy;dbname=db1',
'emulatePrepare' => false,
'username' => 'user1',
'password' => 'pass1',
'schemaCachingDuration' => YII_DEBUG ? 0 : 86400000, // 1000 days
'enableParamLogging' => YII_DEBUG,
'charset' => 'utf8'
),
'db2'=>array(
'class' => 'CDbConnection',
'connectionString' => 'pgsql:host=xxx.xxx.xxx;port=xxxx;dbname=db2',
'emulatePrepare' => false,
'username' => 'user2',
'password' => 'pass2',
'schemaCachingDuration' => YII_DEBUG ? 0 : 86400000, // 1000 days
'enableParamLogging' => YII_DEBUG,
'charset' => 'utf8'
),
If you are in CActiveRecord instance ($model), you can use $model->getDbConnection(); or CActiveRecord::$db to get current connection database;
In class reference you can read:
public array getComponents(boolean $loadedOnly=true)
So, if you want to get every connection instead of only the actives:
foreach (Yii::app()->getComponents(false) as $component)
{
if (is_array($component) && $component['class'] == 'CDbConnection')
{
print_r($component);
}
}
Should list all

CakePHP doesn't create tests tables

I've tried to do some tests, but cakephp doesn't create de test_{tablename} tables! He is trying to use de original tables.
Database config:
var $test = array(
'driver' => 'mysql',
'persistent' => false,
'host' => '127.0.0.1',
'login' => 'root',
'password' => '',
'database' => 'tests_clubpets',
'encoding' => 'utf8'
);
Fixture:
class AdminFixture extends CakeTestFixture {
var $name = 'Admin';
var $fields = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
...
'tableParameters' => array('charset' => 'utf8', 'collate' => 'utf8_general_ci', 'engine' => 'InnoDB')
);
var $records = array(
array(
'id' => 1,
'nome' => 'Lorem ipsum dolor sit amet',
...
);
}
Model:
class AdminTestCase extends CakeTestCase {
var $fixtures = array('app.admin');
function startTest() {
$this->Admin =& ClassRegistry::init('Admin');
}
function endTest() {
unset($this->Admin);
ClassRegistry::flush();
}
}
What can be wrong?
when you create the $test database connection, cake should be trying to make the tables in that database. according to your code that should be in tests_clubpets. make sure the database is created and that the user has permissions on the table. also check you did not make any typeo's
if you want to have your tables named test_{tablename}, use:
'prefix' => 'test_',

Categories