How to join three tables and get value in grid view - mysql

I have three tables :
contacts hasMany groups
contact_groups hasMany contacts
contact_contact_groups
columns in table contact
contact_id | contact_name
columns in table contact_groups
group_id | group_name
columns in table contact_contact_groups
contact_contact_group_id | contact_id | group_id
MODEL
contacs model
public function getContactContactGroups()
{
return $this->hasMany(ContactContactGroups::className(),
['contact_id' => 'contact_id']);
}
contact_groups model
public function getContactContactGroups()
{
return $this->hasMany(ContactContactGroups::className(),
['group_id' => 'group_id']);
}
contact_contact_groups model
public function getGroup()
{
return $this->hasOne(ContactGroups::className(), ['group_id' => 'group_id']);
}
public function getContact()
{
return $this->hasOne(Contacts::className(), ['contact_id' => 'contact_id']);
}
I want to display grid like this :
-----------------------------
Contact Name | Group Name
-----------------------------
Me | Uncategorized
Mother | Family
Jhon | Business
VIEW
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'tableOptions' =>['class' => 'table table-striped table-bordered'],
'columns' => [
[
'attribute' => 'contact_name',
'value' => 'contact_name',
],
[
'attribute' => 'contactContactGroups.group_id',
'value' => 'contactContactGroups.group.group_name',
'filter' => Html::activeDropDownList($searchModel, 'group_id', ArrayHelper::map(ContactGroups::find()->where(['group_status'=>'ACTIVE'])->asArray()->all(), 'group_id', 'group_name'),['class'=>'form-control','prompt' => 'Select Group']),
],
],]);
?>
ContactsController
public function actionIndex() {
$this->unsetThisButton(array(4,5));
$searchModel = new ContactsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
But it showing (not set) not a group_name .

A simple way is based on adding a getter in your model eg: for contact_contact_groups model
you have a relation
public function getGroup()
{
return $this->hasOne(ContactGroups::className(), ['group_id' => 'group_id']);
}
use a getter for group_name
public function getGroup_group_name() {
return $this->group->group_name;
}
and in grid view in the attribute
[
'attribute' => 'group_group_name',
'filter' => Html::activeDropDownList($searchModel, 'group_id', ArrayHelper::map(ContactGroups::find()->where(['group_status'=>'ACTIVE'])->asArray()->all(), 'group_id', 'group_name'),['class'=>'form-control','prompt' => 'Select Group']),
],
do the same for the relation and field

I Found simple stuff like this :)
GRID (VIEW)
[
'attribute' => 'contactContactGroups.group_id',
'value'=>function ($data) {
$d = array();
foreach ($data->contactContactGroups as $k=>$m)
{
$d[] = ContactContactGroups::get_group_name_by_id($m->group_id);
}
return implode($d, ', ');
},
'filter' => Html::activeDropDownList($searchModel, 'group_id', ArrayHelper::map(ContactGroups::find()->where(['group_status'=>'ACTIVE'])->asArray()->all(), 'group_id', 'group_name'),['class'=>'form-control','prompt' => 'Select Group']),
],
models/ContactContactGroups.php Model
I create function get_group_name_by_id($id)
public static function get_group_name_by_id($id){
$model = ContactGroups::find()->where(["group_id" => $id])->one();
if(!empty($model)){
return $model->group_name;
}
return null;
}
so the result is :
Contact | Category
-------------------------------
Me | Business, Family
Erick | Business, Office
Jhon | Office
Thank's #scaisEdge, you give me some clue ;)

Related

ArrayDataProvider gridview yii2

I have this
public function actionIndex()
{
$data = order::getSome();
$dataProvider = new ArrayDataProvider([
'allModels' => $data,
'sort' => [
'attributes' => ['paid']
]
]);
return $this->render('index',['dataProvider' =>$dataProvider]);
}
If in gridview delete 'columns'=> [, there is post all columns in data correct. If in 'columns'=> [ put names of columns, there is '0' in all columns. How to display specific columns?
`public static function getSome(){
return Yii::$app->getDb()->createCommand(
"SELECT order_customFields.order_customFields_delivery_method,
sum(case `order`.order_status when 'paid' then 1 else 0 end) paid,
sum(case `order`.order_status when 'later' then 1 else 0 end) later,
sum(case `order`.order_status when 'delivery-approved' then 1 else 0 end) deliveryapproved,
FROM order_customFields
INNER JOIN `order` ON order_customFields.order_id = `order`.order_id
WHERE
order_customFields.order_customFields_order_date >= '2016-12-01' AND
order_customFields.order_customFields_order_date <= '2016-12-31'
AND order_customFields.order_customFields_delivery_method is not null
GROUP BY
order_customFields.order_customFields_delivery_method"
)->queryAll();
}`
based on your sample could be you are using an activeRecord Order (and not order)
Order::getSome();
this function give you the related models so why you are not use the canonical way for yii2 like
/**
* Lists all AntigoneResidente models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new OrderSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('#common/views/antigone-residente/index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
then in gridview you can access to specific column
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'id',
'your_column_name1',
'your_column_name2',
.....
['class' => 'yii\grid\ActionColumn',],
],
]); ?>
If you want to use arrayDataProvider then you have to pass data in array format not in object
public function actionIndex()
{
$data = order::getSome()->asArray();
$dataProvider = new ArrayDataProvider([
'key'=>'id',
'allModels' => $data,
'sort' => [
'attributes' => ['id','paid','vendorid','orderno'],
],
]);
}
In your Gridview
echo GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'paid',
'vendorid',
'orderno'
],
]
]);

YII2 creating relations in models between tables from 2 databases

I have defined 2 databases , for example
return [
'components' => [
'db1' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db1name',
'username' => 'db1username',
'password' => 'db1password',
],
'db2' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db2name',
'username' => 'db2username',
'password' => 'db2password',
],
],
];
Now i have a table as 'users' in 'db1' and table 'countries' in 'db2'
users
id , country_code , username , password
1 , DE , xyz , 12345
2 , FR , abc , 12345
countries
code , name
DE , Germany
FR , France
IN , India
I have defined the foreign key relation between users.country_code & countries.code
ISSUE
But when i try to create the model for 'users' table using gii it gives an error , possibly because the tables relation are from 2 different databases.
How to use tables from different databases in relations of a model.
Any suggestions are welcomed
This works in my case to list iten on GridView::widget
-> bd_sisarc is my secound data base
-> deposito_sondagem is a table from my first data base
public static function getDb() // on your model
{
return Yii::$app->get('db1');
}
public static function getDb() // on your model
{
return Yii::$app->get('db2');
}
public function getEmpresaSondagem() // Relation on you model
{
return $this->hasOne(EmpresaSondagem::className(), ['idEmpSondagem' => 'entidade_deposito']);
}
public function search($params)
{
$this->load($params);
$sql = "SELECT deposito_sondagem.*
FROM
deposito_sondagem,
`bd_sisarc`.`tbempresasondagem`
WHERE
`bd_sisarc`.`tbempresasondagem`.`idEmpSondagem`=`deposito_sondagem`.`entidade_deposito`
and deposito_sondagem.estado=1
and tbempresasondagem.estado=1
and numero_registo LIKE '%$this->numero_registo%'
and nomeempsondagem LIKE '%$this->nomeEntidade%'
and dono_sondagem LIKE '%$this->dono_sondagem%'
and data_deposito LIKE '%$this->data_deposito%'";
$query = DepositoSondagem::findBySql($sql);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
return $dataProvider;
}
Try this one
SELECT `users`.* FROM `users` LEFT JOIN `db2name`.`countries` ON `users`.`country_code` = `db2name`.`countries`.`code `

json column to gridview in yii2

I have a table:
id | name | values |
1 | John | {"data1":{"key1":"abs", "key2":"qwe"}, "data2":{"key1":"asd","key2":"obj"}}
in each row the json has different length. I need to create gridView like:
id | name | abs | asd | ...
1 | John | qwe | obj | ...
My code SqlDataProvider:
$count = Yii::$app->db->createCommand('
SELECT COUNT(id) FROM statistics', [':status' => 0])- >queryScalar();
$dataProvider = new SqlDataProvider([
'sql' => 'SELECT id, name, value
FROM
statistics',
'totalCount' => $count,
'key' => 'id',
]);
return $this->render('statistics',
[
'dataProvider' => $dataProvider,
]);
and GridView:
GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
'name',
],
]); ?>
When I add column 'vlues' the result was like:
1 | John | {"data1":{"key1":"abs", "key2":"qwe"}, "data2":{"key1":"asd","key2":"obj"}}
Please, help!
I think you can add value column like this
'columns' => [
'id',
'name',
[
'attribute' => 'data1',
'value' => function($model){
return (json_decode($model->value)->data1);
}
],
],

Laravel - Eloquent overwriting a custom timestamp... WHY?

I am making an inventory management system.
When a product is out of stock, I make an entry in a table and note the "oos_at" field with the date/time.
later, when it is back in stock, i find that entry and update the "restocked_at" timestamp field.
However, when I do the second action, my "oos_at" field is overwritten with the timestamp of the query ('updated_at') field...
I had to keep track of this "oos_at" field on another entity so it wouldn't get overwritten and then use that field to update the "oos_at" field a second time when updating that table...
Below is my code....
class Pusher extends Model {
/**
*
*
* #param Carbon $oos_at
* #return bool
*/
public function setAsOutOfStock(Carbon $oos_at)
{
Log::info([
'FILE' => get_class($this),
'method' => 'setAsOutOfStock',
'pusher' => $this->id,
'param:oos_at' => $oos_at->toDateTimeString(),
]);
$this->pusherOutOfStocks()->create([
'location_id' => $this->location_id,
'product_id' => $this->product_id,
'oos_at' => $oos_at->toDateTimeString(),
]);
$this->oos = true;
$this->oos_at = $oos_at;
return $this->save();
}
/**
* Clear the PusherOutOfStocks attached to $this Pusher
* (This Pusher has been restocked!)
*
* #param Carbon $time
* #return bool
*/
public function setAsInStock(Carbon $time)
{
Log::info([
'FILE' => get_class($this),
'method' => 'setAsInStock',
'pusher' => $this->id,
'param:time' => $time->toDateTimeString(),
]);
$this->pusherOutOfStocks()->where('restocked_at', null)
->update(['restocked_at' => $time->toDateTimeString()]);
$this->oos = false;
$this->oos_at = null;
return $this->save();
}
}
When I die and dump the PusherOutOfStocks BEFORE the pusher is restocked, then the "oos_at" is set appropriately.
Illuminate\Database\Eloquent\Collection {#340
#items: array:1 [
0 => Newave\Engineering\PusherOutOfStock {#343
#fillable: array:5 [
0 => "pusher_id"
1 => "location_id"
2 => "product_id"
3 => "oos_at"
4 => "restocked_at"
]
#dates: array:2 [
0 => "oos_at"
1 => "restocked_at"
]
#connection: null
#table: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:9 [
"id" => 246
"pusher_id" => 216
"location_id" => 634
"product_id" => 378
"oos_at" => "2016-03-11 03:00:00"
"restocked_at" => null
"created_at" => "2016-03-11 12:12:01"
"updated_at" => "2016-03-11 12:12:01"
"deleted_at" => null
]
#original: array:9 [
"id" => 246
"pusher_id" => 216
"location_id" => 634
"product_id" => 378
"oos_at" => "2016-03-11 03:00:00"
"restocked_at" => null
"created_at" => "2016-03-11 12:12:01"
"updated_at" => "2016-03-11 12:12:01"
"deleted_at" => null
]
#relations: []
#hidden: []
#visible: []
#appends: []
#guarded: array:1 [
0 => "*"
]
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
#forceDeleting: false
}
]
}
When I die and dump the PusherOutOfStocks AFTER the pusher is restocked, then the "oos_at" is the same as "updated_at"
Illuminate\Database\Eloquent\Collection {#408
#items: array:1 [
0 => Newave\Engineering\PusherOutOfStock {#775
#fillable: array:5 [
0 => "pusher_id"
1 => "location_id"
2 => "product_id"
3 => "oos_at"
4 => "restocked_at"
]
#dates: array:2 [
0 => "oos_at"
1 => "restocked_at"
]
#connection: null
#table: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:9 [
"id" => 244
"pusher_id" => 214
"location_id" => 626
"product_id" => 374
"oos_at" => "2016-03-11 12:10:23"
"restocked_at" => "2016-03-11 04:00:00"
"created_at" => "2016-03-11 12:10:22"
"updated_at" => "2016-03-11 12:10:23"
"deleted_at" => null
]
#original: array:9 [
"id" => 244
"pusher_id" => 214
"location_id" => 626
"product_id" => 374
"oos_at" => "2016-03-11 12:10:23"
"restocked_at" => "2016-03-11 04:00:00"
"created_at" => "2016-03-11 12:10:22"
"updated_at" => "2016-03-11 12:10:23"
"deleted_at" => null
]
#relations: []
#hidden: []
#visible: []
#appends: []
#guarded: array:1 [
0 => "*"
]
#dateFormat: null
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
+wasRecentlyCreated: false
#forceDeleting: false
}
]
}
I have even used DB::getQueryLog() and saw NOWHERE that 'oos_at' was being explicitly set/updated....
Nowhere else in my code is this table modified...
Does anyone understand what is happening here????
Thank you!!
=============
Extra Code Snippets
Table Migration::
Schema::create('pusher_out_of_stocks', function (Blueprint $table) {
$table->increments('id');
$table->integer('pusher_id');
$table->integer('location_id');
$table->integer('product_id');
$table->timestamp('oos_at');
$table->timestamp('restocked_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
PusherOutOfStock Class
class PusherOutOfStock extends Model
{
use SoftDeletes;
protected $fillable = [
'pusher_id',
'location_id',
'product_id',
'oos_at',
'restocked_at',
];
protected $dates = [
'oos_at',
'restocked_at'
];
/**
* A PusherOutOfStock belongsTo a Product
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function product()
{
return $this->belongsTo(Product::class);
}
/**
* A PusherOutOfStock belongsTo a Pusher
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function pusher()
{
return $this->belongsTo(Pusher::class);
}
/**
* A PusherOutOfStock belongsTo a Location
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function location()
{
return $this->belongsTo(Location::class);
}
}
Below is the sequence the code goes through to hit that update method
First, it hits my InventoryController in my API namespace
public function upload(Request $request)
{
$data = $request->all();
$header = $this->getHeader($data);
$body = $this->getBody($data);
$reader = $this->getReaderFromHeader($header);
if ( ! $this->guardAgainstNoReader($reader))
return (new Response("Reader with mac-address: ". $header['reader_mac'] . " does not exist.", 409));
$result = $this->inventoryRepository->uploadPusherData($reader, $header, $body);
return $result;
}
Then it sends the request to the InventoryRepository
public function uploadPusherData(Reader $reader, $header, $body)
{
foreach ($body as $pusherData)
{
$result[] = $this->processPusherData($pusher, $header['timestamp'], $pusherData);
}
return $result;
}
Then, inside the repository, it processes one line at a time (in my test, there is only one line)
private function processPusherData(Pusher $pusher, $timestamp, $pusherData)
{
$latestInventory = $pusher->latestInventory;
if (! $latestInventory)
return $this->updatePusher($pusher, $timestamp, $pusherData);
if ($latestInventory->tags_blocked == $pusherData['data_TAGSBLKED'])
return $this->noChangeRecorded($pusher, $latestInventory, $timestamp);
return $this->updatePusher($pusher, $timestamp, $pusherData);
}
Then the pusher is updated...
public function updatePusher($pusher, $timestamp, $pusherData)
{
// See if there are any existing already
$prevInv = $pusher->latestInventory;
// Create the new data
$inventory = Inventory::create([
'pusher_id' => $pusher->id,
'product_id' => $pusher->product_id,
'reader_id' => $pusher->reader->id,
'tags_blocked' => $pusherData['data_TAGSBLKED'],
'paddle_exposed'=> $pusherData['paddle_exposed'],
'created_at' => Carbon::createFromTimestamp($timestamp)
->toDateTimeString(),
]);
if ( !$prevInv || $prevInv->id == $inventory->id )
{
return "first-data" . $timestamp;
}
return $this->checkForEvents($inventory, $prevInv);
}
We check if any events should be triggered... In this case, previous inventory had 9 items in stock... now there are 0.
private function checkForEvents(Inventory $currentInventory, Inventory $previousInventory)
{
if ( ! $previousInventory->oos && $currentInventory->oos && $previousInventory->pusher->oos_notified == 0)
{
$currentInventory->pusher->oos_notified = true;
$currentInventory->pusher->save();
return Event::fire(new InventoryOutOfStock($currentInventory));
}
if ( ( $previousInventory->oos || $previousInventory->status == "RESTOCK" )
&& $currentInventory->tags_blocked > 2 )
{
return Event::fire(new PusherWasRestocked($currentInventory));
}
if ( $currentInventory->status == "RESTOCK" && $previousInventory->pusher->low_stock_notified == 0)
{
$currentInventory->pusher->low_stock_notified = true;
$currentInventory->pusher->save();
return Event::fire(new LowStockAlert($currentInventory));
}
return "no-events";
}
This then fires the event InventoryOutOfStock
That triggers 3 events... 2 are related to notifications being sent etc..
'App\Events\InventoryOutOfStock' => [
'App\Listeners\InventoryOutOfStockUpdater',
'App\Listeners\EmailInventoryOutOfStockNotification',
'App\Listeners\SMSInventoryOutOfStockNotification',
// 'App\Listeners\OutOfStocksUpdater',
],
Which leads us to ...
public function handle(InventoryOutOfStock $event)
{
$pusher = $event->pusher;
$inventory = $event->inventory;
$product = $pusher->product;
$oos = $pusher->setAsOutOfStock($inventory->created_at);
$locationPushers = $product->getPushersByLocation($pusher->location);
$isInStock = false;
foreach ($locationPushers as $pusher)
if ($pusher->oos == 0)
$isInStock = true;
if (! $isInStock)
$product->productOutOfStocks()->create([
'location_id' => $pusher->location_id,
'oos_at' => $event->inventory->created_at,
]);
}
I think that you do not have to use the timestamp method to create your fields, but you should use the dateTime method:
Schema::create('pusher_out_of_stocks', function (Blueprint $table) {
$table->increments('id');
$table->integer('pusher_id');
$table->integer('location_id');
$table->integer('product_id');
$table->dateTime('oos_at');
$table->dateTime('restocked_at')->nullable();
$table->timestamps();
$table->softDeletes();
});
This should work :)
Hope it helped!

How to use date format to get month and year in Yii2

Let's say I have a table
Date Item Qty
15/1/2016 Item1 10
16/1/2016 Item1 20
16/2/2016 Item1 30
18/2/2016 Item1 10
And In the report I want to display like this
Month Qty
01-2016 30
02-2016 40
Please tell me how can I do this in Yii2.
I've tried the following in my SearchModel.
$query = Productsalesdetails::find()
->select(['DATE_FORMAT(date, "%m-%Y"),productname,sum(total) as total'])
->groupBy('DATE_FORMAT(date, "%m-%Y")');
For the query use letteral select and alias for date_format too..
$query = Productsalesdetails::find()
->select( 'DATE_FORMAT(date, "%m-%Y") as m_date, productname, sum(total) as total')
->groupBy ('m_date, productname');
Do the fact you have already the format in select you can use the alias in attributes
<?= GridView::widget([
'dataProvider' => $dataProvider,
'summaryOptions' => ['class' =>'dfenx_pagination_summary',],
'pager' => ['options' => ['class'=> 'pagination pull-right']],
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
[
'attribute' => '',
'label' => 'Month',
'value' => function ($model) {
return $model->m_date;
},
],
[
'attribute' => 'productname',
'label' => 'Item',
],
[
'attribute' => '',
'label' => 'Total',
'value' => function ($model) {
return $model->total;
},
],
....
]);
this should work without adding $var in model for alias ..
otherwise you your model you need
class YourModel extends \yii\db\ActiveRecord
{
public $m_date;
public $total;
.....