Yii display multiple Child elements as links in a CGridView Field - php

I have the following table:
CREATE TABLE "place" (
"id" int8 NOT NULL DEFAULT nextval('place_id_seq'::regclass),
"name" varchar(128) NOT NULL,
"parent" int8,
"description" varchar(100)
)
WITH (OIDS=FALSE);
(It's PostgreSQL but this shouldn't be relevant here)
I created the CRUD through giix and changed a bit the relations to match my needs. So I ended up with:
public function relations() {
return array(
'parent0' => array(self::BELONGS_TO, 'Places', 'parent'),
'places' => array(self::HAS_MANY, 'Places', 'parent'),
);
}
The goal here is that a place can belong to another place and have multiple children places.
My problem is that I need to change the admin action to match the following grid:
Print manipulated by the inspector to reflect the desired behaviour
So my problem is to get the list of all the children objects and display it as a list of links. I tried change the respective admin.php view file for:
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'places-grid',
'dataProvider' => new CActiveDataProvider('Places', array('id'=>$model->places)),
'filter' => $model,
'columns' => array(
array(
'name'=>'parent',
'value'=>'GxHtml::valueEx($data->parent0)',
'filter'=>GxHtml::listDataEx(Places::model()->findAllAttributes(null, true)),
),
'name',
'places',
array(
'class' => 'CButtonColumn',
),
),
));
But this, as expected, throughs an error:
htmlspecialchars() expects parameter 1 to be string, array given
Also I don't know this would even work with the built in avanced search.
Could anyone kindly point me into the correct direction here?

After much search I found a way to solve my problem. Not sure if it's the best way but it works nicely:
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'places-grid',
'dataProvider' => new CActiveDataProvider('Places', array('id'=>$model->places)),
'filter' => $model,
'columns' => array(
array(
'name'=>'parent',
'value'=>'GxHtml::valueEx($data->parent0)',
'filter'=>GxHtml::listDataEx(Places::model()->findAllAttributes(null, true)),
),
'name',
array(
'header'=>'Children',
'type'=>'html',
'value'=> function($data) {
$placesLinks = array();
foreach ($data->places as $place) {
$placesLinks[] = GxHtml::link(GxHtml::encode(GxHtml::valueEx($place, 'name')), array('places/view', 'id' => GxActiveRecord::extractPkValue($place, true)));
}
return implode(', ', $placesLinks);
}
),
array(
'class' => 'CButtonColumn',
),
),
));
This uses Giix. If you don't use that then you'll have to change the link generation accordingly.
Cheers

Related

Jquery attr doesnt work on Yii

I'm trying to change the element value using Jquery but it's not working...
This is my widget where I have my element 'tag' which i want to change it to textField on edit...
$this->widget('EditableGrid', array(
'dataProvider' => $dataProvider->searchbyID($invoice_id),
'template' => '{items}{buttonCreateRow}{pager} ',
'id' => 'InvoiceLine-grid',
'rowTemplate' => $row_template,
'columns' => array(
array(
'class' => 'EditableGridColumn',
'header' => '',
'name' => 'InvoiceLine_{gridNum}_{rowNum}_edit',
'imageurl'=> Yii::app()->request->baseUrl.'/images/update.png',
'tag' => 'button',
'tagHtmlOptions' => array(
'onclick'=>'editline(this)',
)
),
array(
'class' => 'EditableGridColumn',
'header' => 'StentysRef',
'name' => '[{gridNum}][{rowNum}]stentysproductref',
'tag' => 'laabel',
'tagHtmlOptions' => array(
'style'=>'background-color:#FFF;border-color:#FFF',
'onkeyup'=>'stentysref(this)',
'readonly'=>true
)
),
My Jquery is,
(as you can see the removeAttr works but attr doesn't)
function editline(obj){
$("#InvoiceLine_1_"+row+"_stentysproductref").attr("tag","textField");
$("#InvoiceLine_1_"+row+"_stentysproductref").removeAttr("readonly");
}
Use .prop() and removeProp() which is added in jQuery 1.6, as the .attr() method sometimes took property values into account when retrieving some attributes, which could cause inconsistent behavior.
function editline(obj){
$("#InvoiceLine_1_"+row+"_stentysproductref").prop("tag","textField");
$("#InvoiceLine_1_"+row+"_stentysproductref").removeProp("readonly");
}
API Doc for .prop()
If you are using EditableGrid widget without pointing AR model, then the row id looks like
$('#_1_'+row+'stentysproductref').
I'll take it into consideration for the future updates. Thank you.

Using CakeDC's search plugin to search data with multi-select field

I am trying to implement a search with CakeDC's Search plugin. In this search I have a field which has 'multiple' => 'checkbox' is set. This field allows user to select multiple cities so that they can filter results according to city/cities. What I did in favour to this, I simply specified the 'type' => 'IN' for that field in Searchable Model's $filterArgs. But noting happened it just responded with all result no searching/filtration happened. To get the clear picture of what I have implemented here are the code snippets:
Model.php
public $actsAs = array(
'Search.Searchable'
);
public $filterArgs = array(
'city' => array(
'type' => 'in',
'field' => 'Model.city'
));
search_form.ctp
echo $this->Form->create('Model', array('url' => array('controller' => 'models', 'action' => 'search')));
echo $this->Form->input('city', array(
'multiple' => 'checkbox',
'options' => array(
'city1' => 'city1',
'city2' => 'city2',
'cityn' => 'cityn')
));
echo $this->Form->end('search');
ModelsController.php
public function search() {
$this->layout = 'front_common';
$this->Prg->commonProcess();
$this->Paginator->settings = array(
'conditions' => $this->Model->parseCriteria($this->Prg->parsedParams()),
'limit' => 10
);
$this->set('Data', $this->Paginator->paginate());
}
also once I tried to use a beforeFilter() in ModelsController to implode the city array() with (,) to be used with IN but same all results. I want to ask if there is any other plugin to do this or any hack to do this with cakeDC's search plugin. Please help.
This should work fine, assuming you are passing in the arg to parseCriteria() as a simple array of values.
public $filterArgs = array(
array(
'name' => 'city',
'type' => 'value',
'field' => 'Model.city'
)
);
And you should be able to pass city:houston|dallas|austin in the URL
It should parse that into an array of args => [city => [houston, dallas, austin]]
And parseCriteria() will translate that into the following conditions fragment: [Model.city => [houston, dallas, austin]]
And CakePHP natively translates that into a SQL where fragment: IN(houston, dallas, austin)
Use DebugKit and monitor your SQL... you can debug() at any step of the process, you should get these values.
Here's the full docs on the Search plugin:
https://github.com/CakeDC/search/tree/master/Docs/Documentation
(I use it heavily, and I love how it lets me organize all search filters)

sort column by date TbJSONGridView

I want to sort a column in TbJSONGridView by date , I have this code
$this->widget(
'bootstrap.widgets.TbJsonGridView', array(
'dataProvider' => $model->searchPending(),
'type' => 'striped bordered condensed',
'summaryText' => false,
'cacheTTL' => 10, // cache will be stored 10 seconds (see cacheTTLType)
'cacheTTLType' => 's', // type can be of seconds, minutes or hours
'enablePagination' => true,
'columns' => array(
array(
'name' => 'Pickup Date',
'value' => '$data->carShippeds[0]->pickup_date',
'type'=>'date',
),
))
but the column I get it is from a related model, and the sorting is not working, when I clicked on the column header it does nothing.
what's wrong with the code???
Gabriel
$data->carShippeds[0]->
This shows us that the current model has many cars shipped. You are only showing the first one in there. While that works, Yii has no way of knowing that you are only showing the first value so your sorting has no chance of working. Create a view that only selects your first pickup date, make a join in your criteria with the main model, show the field without [0]-> and it will work.
Change the below section:
array(
'name' => 'Pickup Date',
'value' => '$data->carShippeds[0]->pickup_date',
'type'=>'date',
),
To this:
array(
'name' => 'pickup_date',
'header' => 'Pickup Date',
'value' => '$data->carShippeds[0]->pickup_date',
'type'=>'date',
),
Then in your 'form' when you want to return DataProvider write the specific sorting code for this association like below:
return new DataProvider($query, [
'pagination' => [
'pagesize' => 20,
],
'sort' => [
'sortVar' => 'sort',
'defaultOrder' => ['t.carShippeds.pickup_date asc'],
'attributes' => [
'pickup_date' => [
'asc' => 't.carShippeds.pickup_date',
'desc' => 't.carShippeds.pickup_date DESC',
],
'*',
],
],
]);
Please notice that I did not know the join alias you have for your t.carShippeds or even whether it is t or another entity. replace it or give me more information about your DataProvider and I can help more.

Yii passing data back into a model

I have been working with Yii for the past few months, but I hit a bit of a block:
In my base controller I have a property public $currentUserOrganisations = NULL; which on load populates it with all the logged in users organizations.
Now I have a page where I get all of the organizations and the user can connect to them, but the ones I already have must say "Connected" rather then have the ability to add. I am using the bootstrap TbGridView widget and in my Organisation model created a function getConnectionAction which either returns the anchor OR a label depending if the organization is already connected to the user.
Here is my problem: I can't find a way to access the already loaded user organizations in the Organisation model, because it is a property on my model class.
See below for code:
Action in controller
public function actionNew()
{
$connectionModel = Connection::model();
$organizationModel = Organisation::model();
$this->selectedSubnav = "Add";
$this->render('new', array("connectionModel" => $connectionModel, "organizationModel" => $organizationModel));
}
Here is the TbGridView in the view:
<?php
$this->widget(
'bootstrap.widgets.TbGridView',
array(
'type'=>'striped',
'enableSorting'=>true,
'id' => 'connection-rest-data',
'dataProvider' => $organizationModel->getConnectionsByOrganization($this->currentOrganisation->id),
'ajaxUpdate' => true,
'template'=>"{pager}<br>\n{items}\n{pager}",
'rowHtmlOptionsExpression' => '',
'emptyText' => Yii::t("site", "no_restults_found") . '.',
"itemsCssClass" => "table table-first-column-number data-table display full dataTable transaction_tbl",
'columns' => array(
array(
'header' => Yii::t("site", "id"),
'value' => '$data->id',
'type' => 'raw',
'name' => 'id'
),
array(
'header' => Yii::t("site", "from_unit"),
'type' => 'raw',
'value' => '$data->fromUnit["name"]',
'name' => 'fromUnit'
),
array(
'header' => Yii::t("site", "to_unit"),
'type' => 'raw',
'value' => '$data->toUnit["name"]',
'name' => 'type'
),
array(
'header' => Yii::t("site", "connection_type"),
'type' => 'raw',
'value' => '$data->type["name"]',
'name' => 'type'
),
array(
'header' => Yii::t("site", "ended"),
'type' => 'raw',
'value' => '$data->ended',
'name' => 'ended'
),
array(
'header' => Yii::t("site", "fees"),
'type' => 'raw',
'value' => 'empty($data->fees) ? "none" : $data->fees["fee"]',
'name' => 'fees'
),
array(
'header' => Yii::t("site", "actions"),
'type' => 'raw',
'value' => 'Organisation::model()->getConnectionAction($data->id, \'' . serialize($this->currentUserOrganisations) . '\'")',
'name' => 'fees'
)
),
)
);
?>
And here is the function in the Organisations Model:
public function getConnectionAction ($connectionId)
{
$currentOrganizations = null; //This I need to get the public $currentUserOrganisations = NULL; value
foreach ($currentOrganizations as $org)
{
if($org->id == $connectionId)
{
return CHtml::label(Yii::t("site", "connected"), "", array("style" => "color:green;"));
}
}
return CHtml::link("Add", "/connection/manage/addConnection?id=$connectionId");
}
Much appreciated guys and give me a shout if anything is unclear!
Ok so I managed a work around which is very close to what Martin said.
In the view for the specific field I do:
'value' => 'Organisation::model()->getConnectionAction($data->id, ' . $this->currentOrganisation->id . ')',
The id is the id of the listed organization and the currentOrganization->id is the id of the users current organization.
Then in my model I get all the organizations for the user:
$currentOrganizations = $this->findAllForUserByOrganizationId($currentOrgId);
Foreach through them and check if they match, full function below.
public function getConnectionAction ($organizationId, $currentOrgId)
{
$currentOrganizations = $this->findAllForUserByOrganizationId($currentOrgId);
foreach ($currentOrganizations as $org)
{
if(in_array($organizationId, array($org->fromUnit['id'], $org->toUnit['id'])))
{
return CHtml::label(Yii::t("site", "connected"), "", array("style" => "color:green;"));
}
}
return CHtml::link(Yii::t("site", "add_connection"), "/connection/manage/addConnection?id=$connectionId", array("class" => "createConnection"));
}
I think you are going to want to avoid Active Record for this one. You can use a CSqlDataProvider to load your TbGridView. Use SQL that looks something like this
select
org.organization_name,
user.name
from organization org
left outer join connection x on org.id = x.organization_id
join user on user.id = x.user_id
where user.id = ? or user.id is null
That ? is a parameter marker to allow you to pass in a user_id in a parameter array rather than just concatenating it. Concatenation provides an opening for SQL injection.
Then in your column list for the gid view test the user.name for null and if null display the add button. If not null, display connected.

filter custom columns(having values from custom methods in model) in CGridView

Below is my code in view.php file
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'myGrid',
'dataProvider' => $model->search(),
'filter' => $model,
'enableSorting' => true,
'columns' => array(
array(
'name' => 'id',
'header' => 'ID',
'value' => $data->id,
),
array(
'header' => 'Value',
'value' => '$data->getValue($data->id)', //getValue($id) is a custom method in the model.php file which returns a value after some calculations
'filter' => $model->listOfFilterValues(), //listOfFilterValues() is a custom method in the model.php file which returns a CHtml::listData
),
),
)
);
As you can observe, I am using a custom method in model.php file to get the Value column(because i cannot get it from a query).
The gridView appears and the dropdown list appears. Works fine till now.
The problem is that the filtering using the dropdown (in Value column) doesnt work. (because its not a column from the query output)
And also the sort on the Value column(when i click on the column header) doesnt work.
Is there a way to get this done? Your help is highly appreciated. Thank you.
Try this:
In Model:
class Plans extends BasePlans
{
...
public $planTypeSearch;
...
public function search() {
...
$criteria->compare('plan.plan_type', $this->planTypeSearch, true);
...
),
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
'sort'=>array(
'attributes'=>array(
'planTypeSearch'=>array(
'asc'=>'plan.plan_type',
'desc'=>'plan.plan_type DESC',
),
),
),
);
In your view:
array(
'name'=>'planTypeSearch',
'header'=> 'Plan Type',
'value'=>'(isset($data->plan)) ? $data->plan->plan_type: null',
'filter'=>isset($plans) ? CHtml::listData($plans, 'plan_type','plan_type') : NULL,
),

Categories