Sorry for the awkward title but I have no better naming for the issue (comments on how to properly name the question are welcome).
Let's say I have 3 Documents:
Category
Product
Version
A Category has many Products. A Product has many Versions.
Now I want to Query for all Categories and list all Products and Versions of each Category.
I know about priming priming.
Is it possible to write a query like:
$qb = $dm->createQueryBuilder('Category')
->field('products')->prime(true)
->field('products.versions')->prime(true)
$query = $qb->getQuery();
Alright it seems that in the current state doctrine-odm does not support multi-level priming. This is a known issue on GitHub.
I found a solution in the GitHub Issue that passes a closure to the prime method to allow at least two level priming. Hope this helps someone.
$myPrimer = function(DocumentManager $dm, ClassMetadata $class, array $ids, array $hints) {
$qb = $dm->createQueryBuilder($class->name)
->field($class->identifier)->in($ids);
if ( ! empty($hints[Query::HINT_SLAVE_OKAY])) {
$qb->slaveOkay(true);
}
if ( ! empty($hints[Query::HINT_READ_PREFERENCE])) {
$qb->setReadPreference($hints[Query::HINT_READ_PREFERENCE], $hints[Query::HINT_READ_PREFERENCE_TAGS]);
}
$results = $qb->getQuery()->toArray();
$nestedPrimers = array(
'address' => true, // List of fields to prime
);
$uow = $dm->getUnitOfWork();
$referencePrimer = new ReferencePrimer($dm, $uow);
foreach ($nestedPrimers as $fieldName => $primer) {
$primer = is_callable($primer) ? $primer : null;
$referencePrimer->primeReferences($class, $results, $fieldName, $hints, $primer);
}
};
The Closure can the be passed to the Primer:
->field('club')->prime($myPrime)
Related
I thought it would work find, but it doesn't.
I have a method in my modelclass like this:
public function getUnitbyName2($unitname, $ProjectID)
{
//$id = (int) $id;
$rowset = $this->tableGateway->select(['Unitname' => $unitname], ['ProjectID' => $ProjectID]);
$row = $rowset->current();
if (! $row) {
// throw new RuntimeException(sprintf(
// 'Could not find row with identifier %d',
// $unitname
// ));
$row=0;
}
return $row;
}
If I give an existing unitname and a non existent project_ID I expect to get some 0 value. But I always get the number of the unit in the first project with the given unitname. It is common that the unitname exists in several different projects.
The function is supposed to get the right record if exist using both parameters.
My question is, what's wrong with using 2 parameters connected by AND?
AbstractTableGateway::select() accepts one argument, you are passing 2:
You need to pass 1, combine the 2 arrays.
Change your code to:
$rowset = $this->tableGateway->select(['Unitname' => $unitname, 'ProjectID' => $ProjectID]);
Zend table Gateways
Basically what i want to do is write this query with cakephp 3 query builder :
SELECT * FROM question as q innerjoin answers as a where q.question = $question AND a.id NOT IN = $avoidedIDs
codes for table class
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use App\Model\Entity\Comlib;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Validator;
use Cake\ORM\TableRegistry;
class ComlibsTable extends Table {
public function initialize(array $config) {
parent::initialize($config);
$this->table('questions');
// join the tables
$this->hasMany('Answers' , [
'foreignKey' => 'question_id'
]);
}
public function LFA( $live_req) {
$query = $this->find()->where(['question' => $live_req])->contain(['Answers'])->LIMIT(6);
$query = $query->toArray();
//include 5 answer
return $query;
}
public function LFM($theid , $avoidedIDs, $question )
{
$query = $this->find()->where(['question' => $question])->contain(['Answers' => function($q){
return $q
->where(['Answers.id NOT IN' => $avoidedIDs] );
}
]);
$query = $query->toArray();
debug($query);
return $query;
}
}
the error that i get is : Impossible to generate condition with empty list of values for field (Answers.id).
but when i print_r($avoidedIDs) i get the values that i passed so im sure that $avoidedIDs is not empty , at least not out of contain function and thats what makes it more complicated for me, but when i put ONE number instead of my variable it will execute , if i put 1,2,3,4 still it will execute only the first one!
what am i doing WRONG for the past 2 days ?????
tnx for any help
It is because you are trying to use $avoidedIDs in an anonymous function (Closure) call, which is not available there.
You should make it available for the function.
->contain(['Answers' => function($q) use ($avoidedIDs){..}
Closures may also inherit variables from the parent scope. Any such
variables must be passed to the use language construct.
http://www.php.net/manual/en/functions.anonymous.php
I have two tables: Empleado and Fichaje in a (1..*) relationship.
I created a query builder for getting fichajes corresponding to IdEmpleado (key) property in Fichaje.
I attempt to filter those Fichajes, but it never works. So I've searched for any clear example of dates in Doctrine for this basic case in vane.
The query result is empty always. No error is thrown.
If I check for IdEmpleado parameter only it gives me all the available records.
The dates interval is the problematic one.
Note: I checked this similar post
Here is the table data, I'm quite convinced of the dates availability.
This is my function:
public function empleadoAction(Request $request){
...
$repository = $em->getRepository('ZkTimeBundle:Fichaje');
$fichajes = $repository->FindByEmpleadoAndDateInterval(
array(
'IdEmpleado' => $workerId,
'FechaInicial' => (new \DateTime('2014-01-10'))->format('Y-m-d'),
'FechaFinal' => (new \DateTime('today'))->format('Y-m-d')
));
...
This is my repository function:
public function FindByEmpleadoAndDateInterval($parameters = array(), $limit = null){
...
$qb = $this->createQueryBuilder('q');
$qb
->where('q.IdEmpleado = :IdEmpleado')
->andWhere('q.Fecha > :FechaInicial')
->andWhere('q.Fecha < :FechaFinal')
->setParameter('IdEmpleado', $parameters['IdEmpleado'])
->setParameter('FechaInicial', $parameters['FechaInicial'])
->setParameter('FechaFinal', $parameters['FechaFinal'])
;
return $qb->getQuery()->execute();
}
Folks, careful with this, have a nice look to the format of dates when you're working with Doctrine 2. The problem was this:
-I've set dates format as: 'Y-M-d', but: 'Ymd' was the correct one (in my particular case).
So, have faith in Doctrine 2 and try every known format (Y-m-d), (Y/m/d), etc. So, you could use dates intervals in these simple ways:
public function findByEmpleadoAndDateInterval($parameters = array(), $limit = null)
{
$qb = $this->createQueryBuilder('q');
$qb->where('q.IdEmpleado = :IdEmpleado')
->andWhere('q.Fecha between :FechaInicial and :FechaFinal')
->setParameters($parameters);
return $qb->getQuery()->execute();
}
OR
public function findByEmpleadoAndDateInterval($parameters = array(), $limit = null)
{
$qb = $this->createQueryBuilder('q');
$qb->where('q.IdEmpleado = :IdEmpleado')
->andWhere('q.Fecha >= :FechaInicial and q.Fecha <= :FechaFinal')
->setParameters($parameters);
return $qb->getQuery()->execute();
}
Maybe, out there, there are more elaborated examples, but this case took me a while to figure it out. Specially because isn't directly accesible online.
I appreciate that this may not be possible, but is there a way to make Zend Paginate go to a specific item (record)?
The result I would like would allow me to seek a specific record in a tabled list of results, and display the appropriate page (within all available pages) combined with a name anchor tag to display the specific record.
To clarify: If I had the results as a Zend_Db_Table_Rowset_Abstract I would use the seek() method in a similar fashion to $rowset->seek(8); Although I don't believe the result returned by the DbSelect adapter is a SeekableIterator?
The code within my Mapper (using the Table Data Gateway pattern):
public function paginate($where = array(), $order = null)
{
$select = $this->getDbTable()->select()->from($this->getTableName(), $this->getTableFields());
foreach ($where as $key => $value) {
$select->where($key, $value);
}
$select->order($order);
$adapter = new Zend_Paginator_Adapter_DbSelect($select);
$paginator = new Zend_Paginator($adapter);
return $paginator;
}
Within my controller:
$cache_id = sha1('list');
$mapper = new Application_Model_Galleries_Mapper();
if(!($data = Zend_Registry::get('cache')->load($cache_id))) {
$data = $mapper->paginate(array(), $sort);
Zend_Registry::get('cache')->save($data, $cache_id, array('list'), 7200);
}
$data->setCurrentPageNumber($this->_getParam('page'));
$data->setItemCountPerPage(30);
$this->view->paginator = $data;
To return a Zend_Paginator with a seekable iterator (Zend_Db_Table_Rowset) use the Zend_Paginator_Adapter_DbTableSelect() as it returns a rowset object, as opposed to Zend_Paginator_Adaoter_DbSelect() which returns an array().
Zend_Paginator
I hope I'm asking this question in an understandable way. I've been working on an app that has been dealing with 1 table ( jobschedule ). So, I have models/Jobschedule.php, models/JobscheduleMapper.php, controllers/JobscheduleController.php, view/scripts/jobschedule/*.phtml files
So in my controller I'll do something like this:
$jobnumber = $jobschedule->getJobnum();
$jobtype = $jobschedule->getJobtype();
$table = $this->getDbTable();
public function listAction()
{
$this->_helper->layout->disableLayout();
$this->view->jobnum = $this->getRequest()->getParam( 'jobnum', false );
$this->view->items = array();
$jobschedule = new Application_Model_Jobschedule();
$jobschedule->setJobnum( $this->view->jobnum );
$mapper = new Application_Model_JobscheduleMapper();
$this->view->entries = $mapper->fetchAll ( $jobschedule );
}
and then in my mapper I I do something like:
$resultSet = $table->fetchAll($table->select()->where('jobnum = ?', $jobnumber)->where('jobtype = ?', $jobtype) );
$entries = array();
foreach ($resultSet as $row) {
$entry = new Application_Model_Jobschedule();
$entry->setJobnum($row->jobnum)
->setJobtype($row->jobtype)
->setJobdesc($row->jobdesc)
->setJobstart($row->jobstart)
->setJobend($row->jobend)
->setJobfinished($row->jobfinished)
->setJobnotes($row->jobnotes)
->setJobid($row->jobid);
$entries[] = $entry;
}
return $entries;
}
Then in my view I can manipulate $entries. Well, the problem I'm coming across now is that there is also another table called 'jobindex' that has a column in it called 'jobno'. That 'jobno' column holds the same record as the 'jobnum' column in the 'jobschedule' table. I need to find the value of the 'store_type' column in the 'jobindex' table where jobindex.jobno = joschedule.jobnum ( where 1234 is the jobno/jobnum for example ). Can someone please help me here? Do I need to create a jobindex mapper and controller? If so, that's done ... I just don't know how to manipulate both tables at once and get the record I need. And where to put that code...in my controller?
If I understand you correctly this is the SQL query you need to extract the data from database:
SELECT `jobschedule`.* FROM `jobschedule` INNER JOIN `jobindex` ON jobindex.jobno = jobschedule.jobnum WHERE (jobindex.jobtype = 'WM')
Assembling this SQL query in Zend would look something like this:
$select->from('jobschedule', array('*'))
->joinInner(
'jobindex',
'jobindex.jobno = jobschedule.jobnum',
array())
->where('jobindex.jobtype = ?', $jobtype);
Let us know if that's what you are looking for.
If I'm understanding you correctly, you'll want to join the 'jobindex' table to the 'jobschedule' table.
...
$resultSet = $table->fetchAll(
$table->select()->setIntegrityCheck(false)
->from($table, array('*'))
->joinLeft(
'jobindex',
'jobindex.jobno = jobschedule.jobnumber',
array('store_type'))
->where('jobnum = ?', $jobnumber)
->where('jobtype = ?', $jobtype)
->where('jobindex.store_type = ?', $_POST['store_num'])
);
....
Depending on how 'jobschedule' is related to 'jobindex', you may want an inner join (joinInner()) instead.
The setIntegrityCheck(false) disables referential integrity between the tables, which is only important if you are writing to them. For queries like this one, you can just disable it and move on (else it will throw an exception).