Sort yii2 gridview by column that's not in a model - sorting

I have Gridview and one column value I get via http request. Is there a way to sort the table by this column?
myTableModel.php
class myTableModel extends \yii\db\ActiveRecord
{
...,
public function getExternalValue() {
$client = new Client();
return $client->createRequest()->setMethod('get')
->setUrl('http:://...')->setData(['id' => 1])->send()->content;
}
}
myTableModelSearch.php
class myTableModelSearch extends myTableModel
{
public function rules()
{
return [
[[...,'externalValue'], 'string'],
[[..., 'externalValue'], 'safe']
];
}
public $externalValue;
public function searchView($params) {
$query = SomeTable::find();
$dataProvider = new ActiveDataProvider(['query' => $query]);
$dataProvider->setSort(['attributes' => [
'externalValue' => [
'asc' => ['externalValue' => SORT_ASC],
'desc' => ['externalValue' => SORT_DESC]
]
]]);
if (!($this->load($params) && $this->validate()))
return $dataProvider;
return $dataProvider;
}
}
view.php
GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
... ,
[
'attribute' => 'externalValue',
'value' => function($item) {
return $item->externalValue;
},
]
],
]);
I also tried to add value to view simply with $item->getExternalValue() (and without public property set), but it makes no difference - when trying to sort I get database exception error SQLSTATE[42S22]: Column not found: 1054 Unknown column 'externalValue' in 'order clause'. How could I trick gridview, to make it sort my table by externalValue column?

You're using yii\data\ActiveDataProvider which uses an instance of ActiveQuery to find its data.
Try using yii\data\ArrayDataProvider, or extend yii\data\ActiveDataProvider to allow a second source for your data.
Additionally, you have to implement a sort function that can sort using your attribute.
see more here and here

Related

Attempt to read property post_id on int

I have a small response from db model, and i colud rebase it and response in route, but i see error.
My Controller:
class PostController extends Controller {
public function getLastRecord() {
$lastRec = Post::latest('created_at')->first();
$res = [];
$res = collect($lastRec)->map(function($record) {
return [
'id' => $record->post_id,
'rec_name' => $record->post_name,
'user' => $record->user_id,
];
})->toArray();
return $res;
}
}
I want that response will be array
['id': 1, 'rec_name': 'test_name', 'user': 1]
instead names from db
['post_id': 1, 'post_name': 'test_name', 'user_id': 1]
Error:
Attempt to read property "post_id" on int
When you collect() a single Record, then call ->map() (or other iterative methods), it loops over your Model's columns, not multiple Post records. You can solve this by wrapping $lastRec in an array, or using ->get() instead of ->first():
$lastRec = Post::latest('created_at')->first();
return collect([$lastRec])->map(function($record) {
return [
'id' => $record->post_id,
'rec_name' => $record->post_name,
'user' => $record->user_id,
];
})->toArray();
// OR
$lastRecs = Post::latest('created_at')->get();
return $lastRecs->map(function ($record) {
return [
'id' => $record->post_id,
'rec_name' => $record->post_name,
'user' => $record->user_id,
];
})->toArray();
Or, since this is a single record (using ->first() only ever returns 1 record), you don't need to collect() or map() at all:
$lastRec = Post::latest('created_at')->first();
return [
'id' => $lastRec->post_id,
'rec_name' => $lastRec->post_name,
'user' => $lastRec->user_id
];

When collection->map returnes array then data in Collection raise error

In laravel 6 app I have collection defined as :
class PermissionCollection extends ResourceCollection
{
public static $wrap = 'permissions';
public function toArray($request)
{
return $this->collection->transform(function($permission){
return [
'id' => $permission->id,
'name' => $permission->name,
'is_checked' => !empty($permission->is_checked) ? $permission->is_checked : null,
'guard_name' => $permission->guard_name,
'created_at' => $permission->created_at,
];
});
}
}
I use it in a control, like :
$permissions = $user->permissions->all();
$userPermissionLabels= Permission
::get()
->map(function ($item) use($permissions) {
$is_checked= false;
foreach( $permissions as $nextPermission ) {
if($nextPermission->permission_id === $item->id) {
$is_checked= true;
break;
}
}
return [ 'id'=> $item->id, 'name'=> $item->name, 'is_checked' => $is_checked];
})
->all();
return (new PermissionCollection($userPermissionLabels));
and I got error :
Trying to get property 'id' of non-object
Looks like the reason is that collection->map returnes array of data, not objects.
If there is a way to fix it without creating new collection(using array) ?
MODIFIED :
I logged loging in my collection,
public function toArray($request)
{
return $this->collection->transform(function($permission){
\Log::info(' PermissionCollection $permission');
\Log::info($permission);
return [
'id' => $permission->id,
'name' => $permission->name,
'is_checked' => !empty($permission->is_checked) ? $permission->is_checked : null,
'guard_name' => $permission->guard_name,
'created_at' => $permission->created_at,
];
});
}
and I see in logs:
PermissionCollection $permission
array (
'id' => 1,
'name' => 'App admin',
'is_checked' => false,
)
local.ERROR: Trying to get property 'id' of non-object
The value is valid array, not null.
I mean I have already use this collenction in other part of the app, can I use it without creating a new one...
I think you get this error because you CollectionResource need to object of the Permission model, but in your case it is trying to get id from an array, after map function. Try to extend your model instead of returning an new array

Verify duplicate values (multi column unique) on the array in Laravel5.7

relate as below issue
Verify duplicate values on the array in Laravel5.7
I am add two fields to data base.
// database/migrations/UpdateUsersTable.php
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('staff_no' , 10);
$table->string('staff_code');
$table->unique(['staff_no', 'staff_code']);
});
}
I want to verify if multi column unique in my database or post array value is duplicate or not?
Here is my codes :
this is my Controller
UsersController
public function MassStore(MassStoreUserRequest $request)
{
$inputs = $request->get('users');
//mass store process
User::massStore($inputs);
return redirect()->route('admin.users.index');
}
and this is my POST data (post data($inputs) will send like as below) :
'users' => [
[
'name' => 'Ken Tse',
'email' => 'ken#gamil.com',
'password' => 'ken12ken34ken',
'staff_no' => '20191201CT',
'staff_code' => 'IT-1azz',
],
[
'name' => 'Tom Yeung',
'email' => 'tom#gamil.com',
'password' => 'tom2222gt',
'staff_no' => '20191201CT', // staff_no + staff_code is duplicate, so need trigger error
'staff_code' => 'IT-1azz',
],
]
MassStoreUserRequest
public function rules()
{
return [
'users' => ['required','array'],
'users.*.name' => ['required'],
'users.*.email' => ['required','unique:users','email', 'distinct'],
'users.*.password' => ['required','string','min:8'],
'users.*.staff_no' => ['required','size:10'],
'users.*.staff_code' => ['required','string']
// how to set verify duplicate values(staff_no,staff_code unique) in here?
];
}
You can use distinct validation rule. So your code will look like-
public function rules()
{
return [
'users' => ['required','array'],
'users.*.name' => ['required'],
'users.*.email' => ['required','unique:users','email', 'distinct'],
'users.*.password' => ['required','string','min:8'],
'users.*.staff_no' => ['required','size:10'],
'users.*.staff_code' => ['required','string', 'distinct']
];
}
Change
`'users.*.staff_code' => ['required','string']` line to
'users.*.staff_code' => ['required','string', Rule::exists('staff')->where(function ($query) {
//condition to check if staff_code and staff_no combination is unique
return $query->where('staff_code', $request->('your_key')['staff_code'])->where('staff_no', $request->('your_key')['staff_no']) ? false : true; // You may need to make a loop if you can not specify key
}),]
I solve this problem myself.
https://laravel.com/api/5.7/Illuminate/Foundation/Http/FormRequest.html#method_validationData
main point is overrides method validationData(),make value "staff_no_code" to validation data.
Here is my codes :
MassStoreUserRequest
public function rules()
{
$validate_func = function($attribute, $value, $fail) {
$user = User::where(DB::raw("CONCAT(staff_no,staff_code )", '=', $value))
->first();
if (!empty($user->id)) {
$fail(trans('validation.alreadyExists'));
}
};
return [
'users' => ['required','array'],
'users.*.name' => ['required'],
'users.*.email' => ['required','unique:users','email', 'distinct'],
'users.*.password' => ['required','string','min:8'],
'users.*.staff_no' => ['required','size:10'],
'users.*.staff_code' => ['required','string']
// 'distinct' check when working with arrays, the field under validation must not have any duplicate values.
// $validate_func check DB exist
'users.*.staff_no_code' => ['distinct',$validate_func]
];
}
//make value "staff_no_code" to validation data
protected function validationData()
{
$inputs = $this->input();
$datas = [];
foreach ($inputs as $input ) {
$input['staff_no_code'] = $input['staff_no'] . $input['staff_code'];
$datas[] = $input;
}
return $datas;
}

Yii2: Labels with attributeLabels() in GridView

I generated a model which got the function attributeLabels():
public function attributeLabels()
{
return [
'column1' => 'name of column1',
'column2' => 'name of column2',
];
}
I also wrote a function in my model, which creates a SQL statement:
public function getReminders()
{
$sql = Yii::$app->db->createCommand('SELECT
[[column1]], [[column2]]
FROM {{%table}}
WHERE column1 > :value')
-> bindValue(':value', '10');
return $sql;
}
Then I created a controller with an actionIndex() function:
public function actionIndex()
{
$model = new TestObject();
$testmodels= new ActiveDataProvider([
'models' => $model->getReminders()->queryAll(),
]);
return $this->render('index', [
'testmodels' => $testmodels,
]);
Finally I created a View
GridView::widget([
'dataProvider' => $testmodels,
'layout' => '{items}{pager}',
'columns' => ['column1', column2,],
]);
For some reason, the column names of the resulting page are column1 and column2. I have tried to create a listView and got the correct names for the columns. How comes that the column names are not set by attributeLabels() in this case? I really don't want to use the label property in the gridview.
Thank you in advance!
If you just need to change the label for the gridview column you can configure you column with proper value for label
GridView::widget([
'dataProvider' => $testmodels,
'layout' => '{items}{pager}',
'columns' => [
[
'attribute'=> column1',
'label' => 'Your Lable for column 1',
],
[
'attribute'=> column2',
'label' => 'Your Lable for column 2',
],
],
]);
you can take a look at thsi for a brief guide http://www.yiiframework.com/doc-2.0/guide-output-data-widgets.html or here for ref http://www.yiiframework.com/doc-2.0/yii-grid-datacolumn.html
I would do the following:
make the base class for the TestObject be ActiveRecord
Override tableName
Simplify getReminders, make it static, and let it return Query not a Command
class TestModel extends yii\db\ActiveRecord
{
...
public static function tableName()
{
return {{%table}};
}
public static function getReminders()
{
$query = self::find()->select(['column1', 'column2'])
->where(['>', 'column1', 10]);
return $query;
}
...
}
Don't set models of the ActiveDataProvier and instead set query
'models' => TestObject::getReminders(),

Showing relation table fields with filters in yii2

I'm showing has many relation table value in my girdview as follows
[
'attribute' => 'Resume Title',
'value' => function($model, $key, $index) {
return implode(\yii\helpers\ArrayHelper::map(
$model->jobSeekerProfiles,
'job_seeker_profile_id',
'resume_title'
));
},
],
my table relation is
public function getJobSeekerProfiles()
{
return $this->hasMany(JobSeekerProfile::className(), ['user_id' => 'user_id']);
}
But how to make this column with filter and sortable ?

Resources