Custom Data Provider using foreach yii2 - yii2

I'm trying to create a custom dataProvider made out of Professor's id’s (teachers id’s). My site hosts information about teachers, courses, grades, etc. for schools.
ER model
The user Alumno (student) will see a gridview of it’s professors for each course he’s registered in, so, I’m trying to return that information in a dataProvider.
First, I ask if the user is an Alumno.
Then I search for the Asignaturas the Alumno is registered in.
With that information, I search for the professors that teaches that Cursos, to return it’s id’s as a dataProvider, so I made a foreach cycle.
What I need is an array of Profesores id’s so I can show Profesors’s names in the grid view. The actual code is querying the last Profesor id into the dataProvider.
public function actionIndex()
{
$this->layout = 'main';
$searchModel = new ProfesorSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
if(User::isUserAlumno(Yii::$app->user->identity->id)){
$alumno = Alumno::find()->where(['id_usuario' => Yii::$app->user->identity->id])->one();
$asignaturas = Asignatura::find()->where(['id_curso' => $alumno->id_curso])->all();
foreach ($asignaturas as $asignatura){
$dataProvider = new ActiveDataProvider([
'query' => Profesor::find()->where(['id' => $asignatura->id_profesor])
]);
}
}
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
Any help would be appreciated.

You can use column() to get all id_profesor from query.
$profesorIds = Asignatura::find()->select('id_profesor')->where(['id_curso' => $alumno->id_curso])->column();
$dataProvider = new ActiveDataProvider([
'query' => Profesor::find()->where(['id' => $profesorIds])
]);

Related

How can I display in view multiple GridViews with one DataProvider group it by some field?

A have a table 'operations' with finance operations for all users, including 'id', 'user_id', 'sum', 'name', 'date_picked' etc.
I have created one ActiveDataProvider that gets all operations for concrete User. Now I would like to display in view data from this dataprovider - not in one GridView, but in several GridViews which data grouped by 'date_picked' value.
I know that I can make a new dataprovider for the each 'date_picked' but there will be a large amount of requests to database.
Is there a way to display grouped data in several GridViews based on one dataprovider's data?
Controller:
/** #var OperationComponent $comp */
$comp = Yii::$app->operation;
$userId = Yii::$app->user->id;
$filterModel = $comp->getOperationSearch();
$operations = $comp->getSearchProvider($userId, Yii::$app->request->get());
OperationComponent:
public function getOperationSearch()
{
return new OperationSearch();
}
public function getSearchProvider($user_id, $params)
{
$model = new OperationSearch();
return $model->search($user_id, $params);
}
OperationSearch:
public function search($user_id, $params)
{
$query = Operation::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
// сортировка по умолчанию
'sort' => [
'defaultOrder' => [
'date_picked' => SORT_DESC
]
]
]);
return $dataProvider;
View:
....
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $filterModel,
.....
]); ?>
....
There is an Yii2 extension for complex GridViews: kartik-v/yii2-grid
It contains collapsible rows for showing details for a row.
You can view a demo here.
What you are looking for is the Expand Row Column Widget. It allows to expand a row by just loading the required data for this row details.
This extension has a good documentation is quite stable and tested and can be used straight forward.

Yii2 virtual attribute out of an attribute of a SqlDataProvider

My AddressController:
public function actionIndex() {
$searchModel = new AddressSearch;
$dataProvider = $searchModel->search($_GET);
return $this->render('index', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
}
My model AddressSearch:
class AddressSearch extends Model {
public function search($params) {
$dataProvider = new SqlDataProvider([
'sql' => '
SELECT
name
FROM...
View:
GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'name2',
I have an attribute called name in this dataProvider. I would like to create a new virtual attribute called name2 out of name by preg_replace()-ing a few parts of it.
The function itself is working. I have tried a lot of different things, but I still can't make it to fill the attribute name2 with data. name2 is always empty. Can you please point me to the right direction? Many thanks!
UPDATE: based on the brilliant ideas of #Imaginaroom and #rob006 I've done the following:
moved getName2() and the attributes I'm filling with SqlDataProvider from base model Address to AddressSearch
deleted the empty base model Address because I don't need it anyway. Less is more!
in search() I've added:
foreach ($dataProvider->getModels() as $row) {
$model = new AddressSearch;
$model->setAttributes($row, false);
$models[] = $model;
}
$dataProvider->setModels($models);
It works! Many thanks guys! It's fantastic that you are there and help!!!
The problem is that you're using SqlDataProvider which returns rows from the table as array instead of model instance. So that's why your getter (virtual-attribute) does not work in GridView - GridView does not work on Address model instance, but on raw array without name2 field.
You should change your SearchModel to use ActiveQuery:
$dataProvider = ActiveDataProvider([
'query' => Address::find()->select(['name']) //and whatever your query contains besides this
])
...
UPDATE: If you don't want to do it like this, you can add your logic directly in GridView like so:
GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
[
'header' => 'Name 2',
'value' => function($model) {
if (isset($this->_name2)) {
return $this->_name2;
}
return $this->_name2 = preg_replace([...
}
]
If you really need Address model instance and use SqlDataProvider at the same time, you may convert array to model instance manually:
$models = [];
foreach ($dataProvider->getModels() as $row) {
$model = Address::instantiate($row);
Address::populateRecord($model, $row);
$models[] = $model;
}
$dataProvider->setModels($models);
You can place this in AddressSearch::search() or create custom SqlDataProvider and override prepareModels().

Yii2 show active() records on a new index page of the same controller

I don't even know how to define my question, so I couldn't really search for an answer, so it would be appreciated if you could point me to the right direction.
I have a model a view and a controller. Normally if I'm calling index, I'm getting all records. That works fine. Now I would like to query from the DB only the active() records. So I would like to see on a new index page only the active records. Where do I have to define this? Of course, the function itself is already defined in ModelQuery. But in Controller, I have to create a new function, in order to be able to call a new view.
public function actionIndex2() {
$searchModel = new ModelSearch;
$dataProvider = $searchModel->search($_GET);
...
return $this->render('index2', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
}
I have tried to add ->active() to all possible places here but no luck. I can't mess with ModelSearch, because then I would ruin basic index site functionality also. It's clear that I can use it when I'm doing so: Order::find()->active(), but now it's not the case. Do I have to create a new ModelSearch also? I hope it's not necessary. Many thanks!
Assuming you want filter by $id eg in your url /index2?id=10) you could try this way
public function actionIndex2($id) {
$searchModel = new ModelSearch;
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider->query->
andWhere(['id' => $id]);
...
return $this->render('index2', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
]);
}
So officially you have to add your ActiveQueries to the search method of your ModelSearch.
public function search($params) {
$query = Model::find()->activeQuery1()->activeQuery2();
$dataProvider = new ActiveDataProvider([
'query' => $query,
...
or
public function search($params) {
$query = Model::find();
$dataProvider = new ActiveDataProvider([
'query' => $query->activeQuery1()->activeQuery2(),
...
This means you have to create a new ModelSearch or a new search method in ModelSearch as many times as many combinations of ActiveQueries you wish to use.

yii2 pagination dose not working Call to a member function offset() on a non-object

I am going to create yii2 pagination and I have following code but it is giving error like Call to a member function offset() on a non-object
public function actionIndex() {
$searchModel = new UsermasterSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider = \common\models\UsermasterSearch::find()->where(['user_id'=>1]);
$countQuery = clone $dataProvider;
$pages = new Pagination(['totalCount' => count($countQuery)]);
$models = $query->offset($pages->offset)->limit($pages->limit)->all();
return $this->render('index', ['searchModel' => $searchModel,'dataProvider' => $dataProvider,]);
}
for models normally create by gii and based on ModelSearch pattern normally the pagination
is already available by defualt complete with all the needed param
eventually you can change the page sise or configure other params
public function actionIndex() {
$searchModel = new UsermasterSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider->query->andWhere( 'user_id 1']);
// eventually you can change the page sise
$dataProvider->pagination->pageSize=5;
return $this->render('index', ['searchModel' => $searchModel,'dataProvider' => $dataProvider,]);
}
in you code there are two definition of $dataProvider ( and i don't know if thw second is a valida dataprovider) you can avoid this simply changing the query attribute eg: adding andWhere to the $dataProvider

display the index for which data is owned by the user who logged in

how to display user data is logged in the index , while other user data does not appear , only users who log you appear
n2 should only user data that appears
public function actionIndex()
{
$searchModel = new CreateBookingsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$model = new Kasbon();
$data = Kasbon::findOne($id);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'model' => $model,
'data' => $data,
]);
}
In your Search model, add a filter in the search function:
$query->andFilterWhere(['user_id' => Yii::$app->user->identity->id]);
public function actionIndex(){
$searchModel = new CreateBookingsSearch();
$session = yii::$app->session;
$id = $session->get('user_id');
$dataProvider = $searchModel->search($id);
return $this->render('index',['searchModel'=>$searchModel, 'dataProvider'=>$dataProvider]);
}