How to enable eager loading on a association in phpactiverecord? - eager-loading

I have an phpactiverecord Model "Category". This Model has a self-referential has_many association, and two has_one associations to another Model.
class Category extends Model {
static $table_name = 'categories';
static $has_many = array(
array(
'child_categories',
'class_name' => 'Category',
'primary_key' => 'categories_id',
'foreign_key' => 'parent_id'
)
);
static $has_one = array(
array(
'german_description',
'class_name' => 'CategoryDescription',
'primary_key' => 'categories_id',
'foreign_key' => 'categories_id',
'conditions' => 'language_id = 2'
),
array(
'english_description',
'class_name' => 'CategoryDescription',
'primary_key' => 'categories_id',
'foreign_key' => 'categories_id',
'conditions' => 'language_id = 1'
)
);
}
I can eager-load the has_one associations when I find Category, this works without any problem:
$category = ActiveRecord\Category::find(
$category_id,
array(
'include' => array( 'german_description', 'english_description' )
)
);
My problem is: When I iterate through all of a category's child-categories, and access the child-categories' has-one associations...
foreach( $category->child_categories as $child_category ){
echo $child_category->german_description->categories_name;
}
..then a new query is run every time I do so, becauce the child-category's has-one associations are not eager loaded.
My question is: Is it possible to configure access a categories child-categories in a way that eager-loads the child-categories' has-one associations?

Related

ManyToMany left join query builder

I have entity A with ManyToMany doctrine relation wite entity B
I want to get data of the entity A using query builder in this way
$data = array(
0 => array(
'entity_A_field_1' => 'entity_A_field_1_value',
'entity_A_field_2' => 'entity_A_field_2_value',
'entity_A_field_3' => 'entity_A_field_3_value',
'entity_B' => array(
0 => array(
'entity_B_field_1' => 'entity_B_field_1_value',
'entity_B_field_2' => 'entity_B_field_2_value',
'entity_B_field_3' => 'entity_B_field_3_value',
),
2 => array(
'entity_B_field_1' => 'entity_B_field_1_value',
'entity_B_field_2' => 'entity_B_field_2_value',
'entity_B_field_3' => 'entity_B_field_3_value',
),
),
),
1 => array(
'entity_A_field_1' => 'entity_A_field_1_value',
'entity_A_field_2' => 'entity_A_field_2_value',
'entity_A_field_3' => 'entity_A_field_3_value',
'entity_B' => null
),
);
her is my entities: entity A is product
class Product
{
/**
* #ORM\ManyToMany(targetEntity="Category", inversedBy="products",cascade={"persist", "remove"})
*
* #ORM\JoinTable(
* name="store_product_category"
* )
*/
private $categories;
Entity B is category
class Category
{
/**
* #ORM\ManyToMany(targetEntity="Product", mappedBy="categories")
*/
private $products;
I want to get all my products with their categories using query builder and without conditions (if they have| null if they don't). i need to get them all.
$data = array(
0 => array(
'product_id' => 'product_id_value',
'product_name' => 'product_name_value',
'product_reference' => 'product_reference_value',
'categories' => array(
0 => array(
'category_id' => 'category_id_value',
'category_name' => 'category_name_value',
'category_token' => 'category_token_value',
),
2 => array(
'category_id' => 'category_id_value',
'category_name' => 'category_name_value',
'category_token' => 'category_token_value',
),
),
),
1 => array(
'product_id' => 'product_id_value',
'product_name' => 'product_name_value',
'product_reference' => 'product_reference_value',
'category' => null
),
);

How to access relationship data in groupgridview with extraRowColumns

The Database Tables:
project_master (id, project_name)
task_master (id, task_name, project_id)
Relationship in the TaskMaster Model:
TaskMaster.php
class TaskMaster extends CActiveRecord
{
/**
* #return array relational rules.
*/
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'ProjectsRpl' => array(self::BELONGS_TO, 'Projects', 'project_id'),
);
}
}
Following GroupGridView view file:
Task.php
$this->widget('ext.groupgridview.GroupGridView', array(
'id' => 'Customer-grid',
'dataProvider' => $modelCustomer->searchCustomer(),
//'mergeColumns' => 'project_id',
'extraRowColumns' => array('ProjectsRpl.project_name'),
'extraRowPos' => 'above',
'afterAjaxUpdate' => 'function(){}',
'columns'=>$columns,
));
GroupGridView reference site.
Getting the following errors:
CException: Column or attribute "ProjectsRpl.project_name" not found!
Only one change in Task.php file.
$this->widget('ext.groupgridview.GroupGridView', array(
'id' => 'Customer-grid',
'dataProvider' => $model->search(),
//'mergeColumns' => 'project_id',
'extraRowColumns' => array('project_id'),
'extraRowPos' => 'above',
'extraRowExpression' => '"<b style=\"color: black\">".$data->ProjectsRpl->project_name."</b>"',
'afterAjaxUpdate' => 'function(){}',
'ajaxUrl' => Yii::app()->createUrl('customer/index'),
'ajaxUpdate' => true,
'enablePagination' => true,
"summaryText" => true,
'enableSorting' => FALSE,
'columns'=>$columns,
));

ZF2: create url aliases in router

I'm new to Zend Framework 2 and i want to learn this framework. I want to create url aliases in router.
For example, I have defined something like this in module.config.php
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'index',
),
),
),
'node' => array(
'type' => 'Application\Controller\AliasSegment',
'options' => array(
'route' => '/node[/:id]',
'constraints' => array(
'id' => '[0-9]+'
),
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'index',
'id' => '0'
),
),
'may_terminate' => true,
),
),
),
When i type www.myapp.local/node/1 it routes to the default action in default controller of my application. What i want is a router extension that can handle aliases for url paths. For example:
www.myapp.local/node/1 = www.myapp.local/aboutus
www.myapp.local/node/2 = www.myapp.local/company/gallery
I know that it was possible in ZF. Here is a link to tutorial how to achieve this in ZF:
friendly urls
I know that this is in Polish but code is self-explanatory i think :)
The idea is to use url helper to assembly valid url using aliases or normal segments (node/[:id])
I've already created AliasSegment class in my Application\Controller folder but it shows me an error:
Fatal error: Uncaught exception 'Zend\ServiceManager\Exception\ServiceNotFoundException' with message 'Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Application\Controller\AliasSegment' in C:\xampp\htdocs\industengine\vendor\zendframework\zendframework\library\Zend\ServiceManager\ServiceManager.php:450 Stack trace: #0
My AliasSegment class (incomplete):
<?php
namespace Zend\Mvc\Router\Http;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
class AliasSegment extends Segment
{
public function match(Request $request, $pathOffset = null)
{
}
}
I was looking for an answer for hours and i couldnt find anything. Please tell me at least what I'm doing wrong, where to insert a code or maybe You know better sollution?
I'm not looking for ready application. I want to learn something but i would appreciate if You can tell me an answer in details :)
Thanks in advance and sorry for my English :)
EDITED:
My custom router is working now. At this moment aliases are hardcoded but it works.
My AliasSegment class looks now:
<?php
namespace Application\Controller;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Mvc\Router\Http;
class AliasSegment extends \Zend\Mvc\Router\Http\Segment
{
public function match(Request $request, $pathOffset = null)
{
$uri = $request->getUri();
$path = $uri->getPath();
//sample logic here
//for /about/gallery uri set node id to 1
//todo: get action, controller and module from navigation
if($path == '/about/gallery'){
$uri->setPath('/node/1');
$request->setUri($uri);
}
return parent::match($request, $pathOffset);
}
protected function buildPath(array $parts, array $mergedParams, $isOptional, $hasChild)
{
if(isset($mergedParams['link'])){
return $mergedParams['link'];
}
return parent::buildPath($parts, $mergedParams, $isOptional, $hasChild);
}
}
In this case /about/gallery is an alias to /node/1. Both adresses are correct. The buildPath function returns alias path correctly. Well, I hope this would be usefull for somebody :)
However i want to setup it in Zend_Navigation with additional parameter named 'link'.
I've done 50% of what i want to achieve however now I have problem to get Zend_Navigation from my router. I don't know how to pass it. I guess it should be something like this:
$sm = $this->getServiceLocator();
$auth = $sm->get('Navigation');
It works in my IndexController but doesnt work in my AliasSegment. I need to find in navigation array nodes with 'link' parameter.
EDIT
I've found solution. The answer is below.
unable to fetch or create an instance for Application\Controller\AliasSegment
if this is controller then I would expect in module.config.php to have:
'controllers' => array(
'invokables' => array(
'\Application\Controller\AliasSegment' => '\Application\Controller\AliasSegment',
)
),
also namespace of your class looks a bit weird:
namespace Zend\Mvc\Router\Http;
what about:
namespace Application\Controller;
OK, I've made it. The important thing for this Thread:
ZF2: How to get Zend\Navigation inside custom route?.
You can use any segment type route. But this may need a little modifications to match function.
If navigation's single page will have 'link' param, the url will be converted to 'link' string but other params will stay behind it. Just think of it as an overlay for default URI of current route.
I had to modify my custom route class a little bit. First of all, i had to change its namespace to Application\Router. Here is a full class:
// EDIT - file within ModuleName/src/Router/Alias.php
namespace Application\Router;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Mvc\Router\Http;
class Alias extends Http\Segment
{
private static $_navigation = null;
public function match(Request $request, $pathOffset = null)
{
$uri = $request->getUri();
$path = $uri->getPath();
$items = self::$_navigation->findAllBy('route', 'node');
$params = null;
if($items != null){
$t = sizeof($items);
for ($i=0; $i < $t; $i++) {
$item = $items[$i];
$params = $item->getParams();
if (isset($params['link']) && $params['link']==$path){
$uri->setPath('/'.$item->getRoute().'/'.$params['id']);
$request->setUri($uri);
break;
}
}
}
return parent::match($request, $pathOffset);
}
public function setNavigation($navigation){
self::$_navigation = $navigation;
}
protected function buildPath(array $parts,
array $mergedParams, $isOptional, $hasChild)
{
if(isset($mergedParams['link'])){
return $mergedParams['link'];
}
return parent::buildPath($parts, $mergedParams,
$isOptional, $hasChild);
}
}
here is sample part of module.config.php:
'navigation' => array(
// The DefaultNavigationFactory we configured in (1) uses 'default' as the sitemap key
'default' => array(
// And finally, here is where we define our page hierarchy
'account' => array(
'label' => 'Account',
'route' => 'node',
'params' => array(
'id' => '2',
),
'pages' => array(
'home' => array(
'label' => 'Dashboard',
'route' => 'node',
'params' => array(
'id' => '8',
'link' => '/about/gallery'
),
),
'login' => array(
'label' => 'Sign In',
'route' => 'node',
'params' => array(
'id' => '6',
'link' => '/signin'
),
),
'logout' => array(
'label' => 'Sign Out',
'route' => 'node',
'params' => array(
'id' => '3',
),
),
),
),
),
),
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'index',
),
),
),
'node' => array(
'type' => 'Application\Router\Alias',
'options' => array(
'route' => '/node[/:id]',
'constraints' => array(
'id' => '[0-9]+'
),
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'index',
'id' => '0'
),
),
'may_terminate' => true,
),
),
),
If it is just for routes like /about and /about/galery then you can simply use literal routes with child routes
'about' => array(
'type' => 'literal',
'options' => array(
'route' => '/about',
'defaults' => array(
'controller' => 'module-controller-about',
'action' => 'index'
)
),
'may_terminate' => true,
'child_routes' => array(
'galery' => array(
'type' => 'literal',
'options' => array(
'route' => '/galery',
'defaults' => array(
'controller' => 'module-controller-galery'
)
)
)
)
)
When it comes to URLs like /blog/1-my-great-seo-title you probably have to set-up a Regex route (which is the slowest, literals are fastest).
Maybe check out DASPRiDs Slides from his Router Presentation

How do I set order by field when using fuelphp orm's has many?

I'm using fuelphp's orm to model my data. How can I control which order my child elements are returned when doing a cascaded find.
For example, here's the example config to attach comments to a post:
protected static $_has_many = array(
'comments' => array(
'key_from' => 'id',
'model_to' => 'Model_Comment',
'key_to' => 'post_id',
'cascade_save' => true,
'cascade_delete' => false,
)
);
How can I say, for example, order the comments by the 'date_entered' field?
Thanks in advance,
David
You can add the order_by clause to the conditions.
protected static $_has_many = array(
'comments' => array(
'key_from' => 'id',
'model_to' => 'Model_Comment',
'key_to' => 'post_id',
'cascade_save' => true,
'cascade_delete' => false,
'conditions' => array(
'order_by' => array(
'field1' => 'DESC',
'field2' => 'ASC',
)
),
),
);
Note that as they are defined in the relation, they are always active and can not be turned off!

Populating CGridView one to many relation

In my project one of the model named types having multiple relation to other models the criteria with part is giving below its like
$criteria->with = array(
'category',
'subCategory',
'accountsSupplierPriceDetails' => array(
'with' => 'serviceLevel'
)
);
the relationship goes like
types - category - relation(1-1)
types - subcategory - relation(1-1)
types - accountsSupplierPriceDetails - relation(1-1)
accountsSupplierPriceDetails - serviceLevel - relation(1-n)
my problem is how to display the names from each table in a grid. the result of (1-n) should be displayed in a dropdownbox. when i try to access the data from column it shows the error Trying to get property of non-object ($data->accountsSupplierPriceDetails->serviceLevel ->name). can anyone please help me.
Thanks in advance.
in my view i create a cgridview with 5 rows in which one row data is populated using dropdownlist.
<div style="width:700px; height:auto;">
<?php
$widget = $this->widget('zii.widgets.grid.CGridView', array(
'id' => 'response-grid',
'dataProvider' => $pickdataset->pickCategorylistingForMain(),
'cssFile' => Yii::app()->baseUrl . '/media/css/gridview.css',
'summaryText' => '',
'ajaxUpdate' => 'response-grid',
'enablePagination' => true,
'pager' => array(
'class' => 'LinkPager',
'cssFile' => false,
'header' => false,
'firstPageLabel' => 'First',
'prevPageLabel' => 'Previous',
'nextPageLabel' => 'Next',
'lastPageLabel' => 'Last',
),
'columns' => array(
array(
'name' => 'id',
'header' => '#',
'value' => '$this->grid->dataProvider->pagination->currentPage * $this->grid->dataProvider->pagination->pageSize + ($row+1)',
),
array(
'type' => 'raw',
'name' => 'categoryExt',
'header' => 'Category',
),
array(
'type' => 'raw',
'header' => 'Sub Category',
'value' => '$data->subCategory->name',
),
array(
'type' => 'raw',
'name' => 'locationExt',
'header' => 'Location',
),
array(
'type' => 'raw',
'header' => 'Name',
'value' => 'CHtml::dropDownList($data->corporate_id."-".$data->category_id."-".$data->sub_category_id."-".$data->location_id,"",popDropBox($data->masterCategoryServiceLevels), array("class" => "listbox"), array("empty" => "Select a Location"))',
),
array(
'type' => 'raw',
'header' => 'Pick',
'value' => 'CHtml::image(Yii::app()->baseUrl . "/media/images/edit_icon.png",$data->corporate_id."-".$data->category_id."-".$data->sub_category_id."-".$data->location_id,array("title" => "Select this Combination"))',
),
),));
function popDropBox($data)
{
$list = array();
foreach ($data as $row)
{
$list[$row->serviceLevel->id] = $row->serviceLevel->name;
}
return $list;
}
?>
</div>
the relation in model is:
'masterCategoryServiceLevels' => array(self::HAS_MANY, 'MasterCategoryServiceLevel', 'category_template_id'),
'corporate' => array(self::BELONGS_TO, 'AccountsCorporate', 'corporate_id'),
'category' => array(self::BELONGS_TO, 'MasterCategory', 'category_id'),
'subCategory' => array(self::BELONGS_TO, 'MasterCategory', 'sub_category_id'),
'location' => array(self::BELONGS_TO, 'MasterLocation', 'location_id'),
the row is
array(
'type' => 'raw',
'header' => 'Name',
'value' => 'CHtml::dropDownList($data->corporate_id."-".$data->category_id."-".$data->sub_category_id."-".$data->location_id,"",popDropBox($data->masterCategoryServiceLevels), array("class" => "listbox"), array("empty" => "Select a Location"))',
),
and i use a function to create dropdown data is
function popDropBox($data)
{
$list = array();
foreach ($data as $row)
{
$list[$row->serviceLevel->id] = $row->serviceLevel->name;
}
return $list;
}
please comment if you have any doubt. i am free to share... have a nice day SO friends... wishes and prayers.