How to specify relationships between tables in ZF2 using ZFcBase - php

i'm new in Zend Framework. I'm working with an application that uses the following db pattern: Controler -> Service -> Mapper.
And i have an entity for each table and a hydrator file. What i need is to join tables in SQL query, but i don´t know how to access the results from the joined table, cause it only feeds the entity of the current table. Here´s my code:
Controller:
$userEntity = new \Portal\Entity\Iamuserdb();
$userEntity->setIamUserDbId($this->params('id'));
$service = $this->getServiceLocator()->get('Portal\Service\Reports');
$user = $service->getUserById($userEntity)
Service (Reports):
protected $iamuserMapper;
public function selectUsers(){
$usuarios = $this->getIamUserMapper()->fetchAllUsers();
return $usuarios;
}
protected function getIamUserMapper()
{
if ($this->iamuserMapper == null) {
$this->iamuserMapper = $this->getServiceManager()->get('Portal\Mapper\Iamuser');
}
return $this->iamuserMapper;
}
Mapper (Iamuser):
public function fetchAllUsers()
{
$select = $this->getSelect();
$select->from('IAMUser')
->join('IAMUser_IAMGroup', 'IAMUser.iamUserDbId=IAMUser_IAMGroup.IAMUser_iamUserDbId', array('IAMUser_iamUserDbId', 'iamGroups_iamGroupDbId'), 'left')
->join('IAMGroup', 'IAMGroup.iamGroupDbId=IAMUser_IAMGroup.iamGroups_iamGroupDbId', array(), 'left');
return $this->select($select);
}
Hydrator:
class Iamuser extends Hydrator
{
protected function getEntity()
{
return 'Portal\Entity\Iamuserdb';
}
protected function getMap()
{
return array(
);
}
}
In my Module.php from the module i have the factory:
'factories' => array(
'Portal\Mapper\Iamuser' => function($sm) {
$dbAdapter = new \Zend\Db\Adapter\Adapter($sm->get('Configuration')['db']);
$mapper = new Mapper\Iamuser();
$mapper->setEntityPrototype(new Entity\Iamuserdb())
->setHydrator(new Mapper\Hydrator\Iamuser())
->setDbSlaveAdapter($dbAdapter)
->setDbAdapter($dbAdapter);
return $mapper;
},
}
See in my mapper i'm joining two tables, but how to fetch these results, since they came from another table and is not feed in Entity class? Thanks in advance

I Solve my problem. All i have to do is return this statement:
return $this->select($select)->getDataSource();
So i skip the hydration results in entities and get raw PDO array, this way i can join tables.

Related

How to dynamically set table name in Eloquent Model

I am new to Laravel. I am trying to use Eloquent Model to access data in DB.
I have tables that shares similarities such as table name.
So I want to use one Model to access several tables in DB like below but without luck.
Is there any way to set table name dynamically?
Any suggestion or advice would be appreciated. Thank you in advance.
Model:
class ProductLog extends Model
{
public $timestamps = false;
public function __construct($type = null) {
parent::__construct();
$this->setTable($type);
}
}
Controller:
public function index($type, $id) {
$productLog = new ProductLog($type);
$contents = $productLog::all();
return response($contents, 200);
}
Solution For those who suffer from same problem:
I was able to change table name by the way #Mahdi Younesi suggested.
And I was able to add where conditions by like below
$productLog = new ProductLog;
$productLog->setTable('LogEmail');
$logInstance = $productLog->where('origin_id', $carrier_id)
->where('origin_type', 2);
The following trait allows for passing on the table name during hydration.
trait BindsDynamically
{
protected $connection = null;
protected $table = null;
public function bind(string $connection, string $table)
{
$this->setConnection($connection);
$this->setTable($table);
}
public function newInstance($attributes = [], $exists = false)
{
// Overridden in order to allow for late table binding.
$model = parent::newInstance($attributes, $exists);
$model->setTable($this->table);
return $model;
}
}
Here is how to use it:
class ProductLog extends Model
{
use BindsDynamically;
}
Call the method on instance like this:
public function index()
{
$productLog = new ProductLog;
$productLog->setTable('anotherTableName');
$productLog->get(); // select * from anotherTableName
$productLog->myTestProp = 'test';
$productLog->save(); // now saves into anotherTableName
}
I created a package for this: Laravel Dynamic Model
Feel free to use it:
https://github.com/laracraft-tech/laravel-dynamic-model
This basically allows you to do something like this:
$foo = App::make(DynamicModel::class, ['table_name' => 'foo']);
$foo->create([
'col1' => 'asdf',
'col2' => 123
]);
$faz = App::make(DynamicModel::class, ['table_name' => 'faz']);
$faz->create([...]);

Laravel, mysql query with join in model class

laravel creates its own model for each table. Since it is one of the most popular frame work, we gone for it. Most of our mysql queries are based on multiple tables, we use join and we write those queries in controller itself. Can some one tell us, how to handle queries with multiple tables in model class.
Thanks in advance.
You have to define relation in your model class, For information regarding relationship see here
E.g. If I want to access State name, in my City page then:
Controller:
$city = App::make('Platform\Storage\City\CityRepository')->view($city_id, false, [], ['state'=>array('id', 'name')])
Repository:
public function view($id, $active=true, $fields=array(), $with=array()) {
$query = $this->model->newQuery();
$tableName = $this->model->getTable();
if ($active) {
$query->where($tableName . '.is_active', '=', 1);
}
if (!$fields) {
$fields = array($tableName . '.*');
}
if ($with) {
$with = $this->model->processWithSelects($with);
$query->with($with);
}
try {
return $query->with($with)->where($tableName . '.id', '=', $id)->first($fields);
}
catch (Exception $e) {
throw new DBException($e->getMessage(), $e->getCode(), $e->getPrevious());
}
}
City Model:
public function state() {
return $this->belongsTo('State');
}
For more info, visit these: Tutorial on Repository, Laravel Query, Simple CRUD
In your Controller, create a constructor:
public function __construct(Model $model) {
parent::__construct();
$this->model = $model; // Model $model, means IOC or Dependancy Injection
}
Then do: $this->model->function_in_your_model(), to execute your model functions.
See, if that helps.

Database switch with BindModel in cakePHP

I'm working on application in cake PHP which uses multiple database. I need to fetch data from multiple tables and i'm using bindModel for their association. But bindModel does not allow database switch functionality, I need to access data from multiple databases.If anyone has done this type of assignment then plz help me out.
In your AppModel
class AppModel extends Model
{
/**
* Connects to specified database
*/
public function setDatabase($database, $datasource = 'default')
{
$nds = $datasource . '_' . $database;
$db = &ConnectionManager::getDataSource($datasource);
$db->setConfig(array(
'name' => $nds,
'database' => $database,
'persistent' => false
));
if ( $ds = ConnectionManager::create($nds, $db->config) ) {
$this->useDbConfig = $nds;
$this->cacheQueries = false;
return true;
}
return false;
}
}
and in your controller you can use
class CarsController extends AppController
{
public function user()
{
$this->User->setDatabase('testdb1');
$cars = $this->User->find('all');
$this->set('User', $User);
}
public function client()
{
$this->Client->setDatabase('testdb2');
$cars = $this->User->find('all');
$this->set('Client', $Client);
}
}

How to handle multidimensional output with (nested) lists using the Zend\Db\TableGateway with a mapper in Zend Framework 2?

I'm developing a RESTful ZF2 based application and using a TableGateway implementation (subclass of the Zend\Db\TableGateway) in combination with a simple mapper for the model, similar to the Album example of the ZF2 manual.
Table class
<?php
namespace Courses\Model;
use ...
class CourseTable {
protected $tableGateway;
public function __construct(TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
}
public function findOnceByID($id) {
$select = new Select();
$where = new Where();
$select->columns(array(
'id',
'title',
'details',
));
$select->from($this->tableGateway->getTable());
$select->where($where, Predicate::OP_AND);
$resultSet = $this->tableGateway->selectWith($select);
return $resultSet;
}
}
Mapper class
<?php
namespace Courses\Model;
use ...
class CourseDetails implements ArraySerializableInterface {
public $id;
public $title;
public $details;
public function exchangeArray(array $data) {
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->title = (isset($data['title'])) ? $data['title'] : null;
$this->details = (isset($data['details'])) ? $data['details'] : null;
}
public function getArrayCopy() {
return get_object_vars($this);
}
}
Controller
<?php
namespace Courses\Controller;
use ...
class CoursesController extends RestfulController // extends AbstractRestfulController
{
protected $acceptCriteria = array(
'Zend\View\Model\JsonModel' => array(
'application/json',
),
'Zend\View\Model\FeedModel' => array(
'application/rss+xml',
),
);
private $courseTable;
public function get($id)
{
$course = $this->getCourseTable()->findOnceByID($id)->current();
$viewModel = $this->acceptableViewModelSelector($this->acceptCriteria);
$viewModel->setVariables(array('data' => array(
'id' => $courseDetails->id,
'title' => $courseDetails->title,
'details' => $courseDetails->details
)));
return $viewModel;
}
...
}
It's working for a shallow output like this:
{
"data":{
"id":"123",
"title":"test title",
"details":"test details"
}
}
But now I need a multidimensional output with nested lists like this:
{
"data":{
"id":"123",
"title":"test title",
"details":"test details",
"events":{
"count":"3",
"events_list":[ <- main list
{
"id":"987",
"date":"2013-07-20",
"place":"Berlin",
"trainers":{
"count":"1",
"trainers_teamid":"14",
"trainers_teamname":"Trainers Team Foo",
"trainers_list":[ <- nested list
{
"id":"135",
"name":"Tom"
}
]
}
},
{
"id":"876",
"date":"2013-07-21",
"place":"New York",
"trainers":{
"count":"3",
"trainers_teamid":"25",
"trainers_teamname":"Trainers Team Bar",
"trainers_list":[ <- nested list
{
"id":"357",
"name":"Susan"
},
{
"id":"468",
"name":"Brian"
},
{
"id":"579",
"name":"Barbara"
}
]
}
},
{
"id":"756",
"date":"2013-07-29",
"place":"Madrid",
"trainers":{
"count":"1",
"trainers_teamid":"36",
"trainers_teamname":"Trainers Team Baz",
"trainers_list":[ <- nested list
{
"id":"135",
"name":"Sandra"
}
]
]
}
]
}
}
}
How / where should I assemble the data to this structure? Directly in the mapper, so that it contains the whole data? Or should I handle this with multiple database requests anb assemple the structure in the controller?
What you are trying to accomplish has nothing to do with the TableGateway-Pattern. The TableGateway-Pattern is there to gain access to the Data of one specified Table. This is one of the reasons why in ZF2 you no longer have the option to findDependantRowsets(). It's simply not the TableGateways Job to do so.
To achieve what you are looking for you have pretty much two options:
1. Joined Query
You could write a big query that joins all respective tables and then you'd manually map the output into your desired JSON Format.
2. Multiple Queries
A little less performant approach (looking at the SQL side of things) but "easier" to "map" into your JSON Format.
To give some insight, Doctrine would go with the multiple query approach by default. This is mostly (i guess!) done to provide features that would work on every data backend possible rather than just a couple of SQL Versions...
Service Class
Since you're wondering about the assembling of the json / array, i would set it up like this
'service_manager' => array(
'factories' => array(
'MyEntityService' => 'Mynamespace\Service\Factory\MyEntityServiceFactory'
)
)
// MyEntityServiceFactory.php
// assuming you only need one dependency! more lines for more dependencies ;)
class MyEntityServiceFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $serviceLocator) {
return new MyEntityService($serviceLocator->get('YourTableGateway'));
}
}
// Your SERVICE Class
class MyEntityService {
// do constructor and stuff to handle dependency
public function someBigQueryAsArray() {
// Query your Gateway here and create the ARRAY that you want to return,
// basically this array should match your json output, but have it as array
// to be used for other stuff, too
}
}
// lastly your controller
public function someAction() {
$service = $this->getServiceLocator()->get('MyEntityService');
$data = $service->someBigQueryAsArray();
// do that viewmodel selector stuff
// ASSUMING $data is a array of more than one baseObject
// i did this in my app to produce the desired valid json output, there may be better ways...
if ($viewModel instanceof JsonModel) {
foreach($data as $key => $value) {
$viewModel->setVariable($key, \Zend\Json\Json::encode($value));
}
return $viewModel;
}
// Handle other ViewModels ....
}

Where do Zend\Db\ResultSet\ResultSet objects store the retrieved data?

In a Zend Framework application, that is built pretty similar to the album application from the ZF2 Getting Started tutorial, I use a ResultSet object to transport the data from the model over the controller to the view (for details see the code below).
This works fine, but I don't get, where the ResultSet object holds the data. I can loop it e.g. with foreach and get the data row byrow (or better model object by model object). But when I debug it in my IDE or simply with var_dump(...), it seems to be empty.
How/where does a Zend\Db\ResultSet\ResultSet object hold the data, retrieved from the database?
The relevant parts of the code:
Module settings:
class Module implements ConfigProviderInterface, ServiceProviderInterface, AutoloaderProviderInterface {
...
public function getServiceConfig() {
try {
return array (
'factories' =>array(
...
'SportTable' => function ($serviceManager) {
$tableGateway = $serviceManager->get('SportTableGateway');
$table = new SportTable($tableGateway);
return $table;
},
'SportTableGateway' => function ($serviceManager) {
$dbAdapter = $serviceManager->get('Zend\Db\Adapter\Adapter');
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Sport());
return new TableGateway('sports', $dbAdapter, null, $resultSetPrototype);
},
...
)
);
}
...
}
Model (table):
class CourseTable {
...
public function findAllByCityNameAndSportTitle($cityName, $sportTitle) {
$select = new Select();
$where = new Where();
...
$resultSet = $this->tableGateway->selectWith($select);
return $resultSet;
}
...
}
Model (mapper):
class Course implements ArraySerializableInterface {
public $id;
...
public function exchangeArray(array $data) {
$this->id = (isset($data['id'])) ? $data['id'] : null;
...
}
...
}
Controller:
class CatalogController extends AbstractActionController {
...
public function listCoursesAction() {
$cityName = $this->params()->fromRoute('city', null);
$sportTitle = $this->params()->fromRoute('sport', null);
return new ViewModel(array(
'city' => $cityName,
'sport' => $sportTitle,
'courses' => $this->getCourseTable()->findAllByCityNameAndSportTitle($cityName, $sportTitle)
));
}
...
}
View:
<?php foreach ($courses as $course) : ?>
<div>
<div>id: <?php echo $this->escapeHtml($course->id); ?></div>
...
</div>
<?php endforeach; ?>
Zend\Db\ResultSet\ResultSet or rather Zend\Db\ResultSet\AbstractResultSet is holding the answer to your question. This object has 10-12 methods most of them providing iterative functionality. To answer your question the Zend\Db\ResultSet\ResultSet holds the information retrieved from the Db in its dataSource paramter (Zend\Db\ResultSet\ResultSet::$dataSource).
The paradox of results being iterated and loaded with foreach but not seen by var_dump() can be explained by the fact that the results returned are actually hold in buffered.
If you want to access the result set I suggest you to use Zend\Db\ResultSet\ResultSet::toArray() ($resultSet->toArray()). This will return an array of arrays where each array is a row in the DB table.
Hope this helps, feedback will be appreciated :),
Stoyan.

Categories