Yii2 Modify find() Method in Model search() - search

I am trying to modify the find() method inside the model search and it throws an error "The data provider property must be set".
Here is my search model:
public function search($params)
{
$userID = Yii::$app->user->identity->id;
$groups = GroupAccess::find()
->where(['user_id' => $userID, 'item_name' => 'group_creator'])
->asArray()
->all();
foreach ($groups as $group) {
$accessGroups[] = $group['group_id'];
}
$query = Group::find($accessGroups);
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
$query->andFilterWhere([
'id' => $this->id,
'status_id' => $this->status_id,
//'created_user_id' => $this->created_user_id,
'created_date' => $this->created_date,
'profile_updated_user_id' => $this->profile_updated_user_id,
'profile_updated_date' => $this->profile_updated_date,
'last_accessed_user_id' => $this->last_accessed_user_id,
'last_accessed_date' => $this->last_accessed_date,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'description', $this->description]);
return $dataProvider;
}
And here is my controller action:
$searchModel = new GroupSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
if (Yii::$app->request->isPjax) {
return $this->renderAjax('groups', [
'searchModel' => $searchModel,
'dataProviderMine' => $dataProvider,
]);
} else {
return $this->render('groups', [
'searchModel' => $searchModel,
'dataProviderMine' => $dataProvider,
]);
}
}
It is important to refine the query as the user should be able to see other groups.
How can i modify the find() method properly?
Thanks.

I see two bugs here:
Your find method
$query = Group::find($accessGroups)
will not work - just replace it with
$query = Group::find()->where(['id' => $accessGroups]);
I guess "The data provider property must be set" error is caused by your view code. E.g. if you are using GridView, you should set its 'dataProvider' widget option:
GridView::widget([
'dataProvider' => $dataProviderMine,
'searchModel' => $searchModel,
'columns' => [
'id', 'status_id', 'created_date' // your view columns here
]
])
Consider also using sub queries in your search method:
$idAccessQuery = GroupAccess::find()
->where(['user_id' => $userID, 'item_name' => 'group_creator'])
->select('group_id');
$query = Group::find()->where([
'id' => $idAccessQuery
]);

Related

Laravel Excel Multiple table primary_id save to another table as a foreign_id

I can successfully import Excel Data in Laravel and Insert into Database multiple tables. I am using maatwebsite/excel version 3 composer package and laravel version 5.8
How can I insert mamber_id value in licence_application table (foreign id) which is member table primary id.
ImportClass file:
class UsersImport implements ToCollection
{
/**
* #param Collection $collection
*/
public function collection(Collection $rows)
{
foreach ($rows as $row){
Member::create([
'full_name' => $row[0],
'father_name' => $row[1],
'mother_name' => $row[2],
'cell_number' => $row[3],
]);
LicenceApplication::create([
'member_id' => ???
'licence_category_name' => $row[4],
]);
LicenceSuccess::create([
'application_type' => $row[5],
]);
}
}
}
Controller file: ImporExcelController.php
public function import(Request $request)
{
$this->validate($request, [
'select_file' => 'required|mimes:xls,xlsx'
]);
$path = $request->file('select_file')->getRealPath();
$data = Excel::import(new UsersImport, $path);
return back()->with('success', 'Import data successfully!');
}
Excel file screenshot: https://imgur.com/a/XhxBRpW
Please help me how to solve this issue?
I solve this issue here is the code.
foreach ($rows as $row){
$memberId = Member::create([
'full_name' => $row['full_name'],
'father_name' => $row['father_name'],
'mother_name' => $row['mother_name'],
'cell_number' => $row['cell_number'],
]);
$applicationId = LicenceApplication::create([
'member_id' => $memberId->member_id,
'licence_category_name' => $row['licence_category_name'],
]);
LicenceSuccess::create([
'member_id' => $memberId->member_id,
'application_id' => $applicationId->application_id,
'application_type' => $row['application_type'],
]);
}

Yii2 pagination doesn't change page

My pager doesn't change to chosen page. When I click to the next page button it reloads the page. I also tried without Pjax container but the result was the same. I red the other posts but still can't figure it out. What I am doing is :
$categoriesProvider = new ActiveDataProvider([
'query' => Page::find()->where(['enable' => 1, 'id_in' => $page_id])->orderBy('sort ASC'),
'pagination' => [
'pageSize' => 1,
'route' => "/".Yii::$app->getRequest()->getQueryParam('page')
]
]);
return $this->render('index', [
'categoriesProvider' => $categoriesProvider
]);
and in the view:
<?php Pjax::begin() ?>
<div class="content-area col-md-8 col-sm-6 col-xs-12 no-padding">
<?php if(!empty($categoriesProvider)){
echo ListView::widget([
'dataProvider' => $categoriesProvider,
'layout' => "{summary}\n{items}
\n<nav class='post-pagination col-md-12 col-sm-12 col-xs-12'>
{pager}
</nav>",
'itemView' => function($model, $key, $index, $widget){
return $this->render('_category', [
'model' => $model,
]);
},
'pager' => [
'nextPageLabel' => ">>",
'prevPageLabel' => "<<",
'maxButtonCount' => 5,
'options' => [
'tag' => 'ul',
'class' => 'pagination',
]
]
]);
}?>
</div><!-- Content Area /- -->
<?php Pjax::end(); ?>
URL configuration:
<?php
namespace frontend\controllers;
use backend\models\News;
use backend\models\Page;
use backend\models\Product;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class SplitterController extends Controller
{
public function actionManageRequest()
{
$param_page = \Yii::$app->getRequest()->getQueryParam('page');
$param_category = \Yii::$app->getRequest()->getQueryParam('category');
$param_product = \Yii::$app->getRequest()->getQueryParam('product');
$page = $this->getModel($param_page, new Page());
//If page is existing page model go forward
if(!empty($page)){
$controller = $this->getController($page->view);
if($this->checkId($param_page) === 'news'){
$model = new News();
}else{
$model = new Page();
}
$category = $this->getModel($param_category, $model);
if(!empty($category)){
$product = $this->getModel($param_product, new Product());
//If product is existing product model - go forward to single view
if(!empty($product)){
$this->registerTags($product);
try{
return \Yii::$app->runAction("$controller/view");
}
catch (\Throwable $e){
throw new NotFoundHttpException('Page not found',404);
}
}
//If product is not empty and product don't - throw exception
if(!empty($param_product) && $product === null){
throw new NotFoundHttpException('Page out found', 404);
}
$this->registerTags($category);
//If page model is news page - render news single view
if($this->checkId($param_page) === 'news'){
return \Yii::$app->runAction("$controller/multi-view");
}
try{
return \Yii::$app->runAction("$controller/multi-view");
}
catch (\Throwable $e){
throw new NotFoundHttpException('Page not found',404);
}
}
$this->registerTags($page);
//If category is not empty but no such page found - throw exception
if(!empty($param_category) && $category === null){
throw new NotFoundHttpException('Page not found', 404);
}
return \Yii::$app->runAction($page->view);
}
//If page is not empty but no such page found - throw exception
if(!empty($param_page) && $page === null){
throw new NotFoundHttpException('Page not found', 404);
}
$page = Page::findOne(13);
$this->registerTags($page);
return \Yii::$app->runAction($page->view);
}
private function getModel($param, $model)
{
$chunks = explode('-', $param);
$chunk_id = end($chunks);
return $model::findOne($chunk_id);
}
private function registerMetaTags($name, $content)
{
\Yii::$app->view->registerMetaTag([
'name' => $name,
'content' => $content
]);
}
private function registerTitle($title)
{
\Yii::$app->view->title = $title;
}
private function checkId($param)
{
$id = explode('-', $param);
$id = end($id);
switch ($id) {
case 14:
return 'news';
break;
default:
return 'page';
break;
}
}
private function getController($path)
{
$controller = explode('/', $path);
return $controller[0];
}
private function registerTags($model)
{
$this->registerMetaTags('description', $model->meta_title);
$this->registerTitle($model->meta_title);
}
}
Url manager rules :
'rules' => [
'<page>/<category>/<product>' => 'splitter/manage-request',
'<page>/<category>' => 'splitter/manage-request',
'<page>' => 'splitter/manage-request',
'' => 'splitter/manage-request'
],
It looks like Pagination param conflict with your URL rules. Pagination uses page GET param to store current page number in URL. But your rules also uses page param and overrides param used by Pagination. You should use different param in your pagination config, to avoid conflicts:
'pagination' => [
'pageSize' => 1,
'route' => "/".Yii::$app->getRequest()->getQueryParam('page'),
'pageParam' => 'paginationPage',
]
See https://www.yiiframework.com/doc/api/2.0/yii-data-pagination#$pageParam-detail
Or you can rename params in your rules to avoid such problem also in other places:
'rules' => [
'<r_page>/<r_category>/<r_product>' => 'splitter/manage-request',
'<r_page>/<r_category>' => 'splitter/manage-request',
'<r_page>' => 'splitter/manage-request',
'' => 'splitter/manage-request'
],

Yii2 - DetailView Search box

I have this action inside ProdutosController. It renders view2 that has an itemview pointing to _view2 which has a DetailView to show products.
public function actionView2()
{
$model = new Produtos();
$dataProvider = new ActiveDataProvider([
'query' => Produtos::find(),
'pagination' => false,
]);
// get the posts in the current page
$posts = $dataProvider->getModels();
// render
return $this->render('view2', [
'dataProvider' => $dataProvider,
'model' => $model,
]);
}
the view2
<?php
use yii\helpers\Html;
use yii\widgets\ListView;
use yii\helpers\Url;
/* #var $this yii\web\View */
/* #var $model app\models\Atividades */
// $this->title = implode('', $cat);
?>
<div class="produtos-view2">
<?php
//use kartik\social\FacebookPlugin;
//echo FacebookPlugin::widget(['type'=>FacebookPlugin::SHARE, 'settings' `=> []]);`
?>
</div>
<?= ListView::widget([
'dataProvider' => $dataProvider,
'itemView' => '_view2',
]); ?>
<br/><br/>
and the _view2 that as DetailView inside:
<?= DetailView::widget([
'model' => $model,
'options' => ['class' => 'detail1-galeria-view2'],
'attributes' => [
// cria um array com a fotografia, em que carrega a path no campo fieldName da bd
[
'attribute'=>'',
//'value'=>$model->foto,
'value'=>Html::a(Html::img(Yii::$app->getUrlManager()->getBaseUrl() . "/" .$model->foto, ['width'=>'192', 'height' => "256"]), $model->foto),
'format' => 'raw',
],
[
'attribute'=>'',
'value'=>$model->nome,
],
[
'attribute'=>'',
'value'=>$model->categoria,
],
[
'attribute'=>'',
'value'=>$model->descricao,
],
[
'attribute'=>'',
'value'=>$model->valor.' '.'€',
],
// info
[
'attribute'=>'',
'format' => 'raw',
'value'=> Html::a(Yii::t('app','Comprar'), Url::toRoute(['contacto/create2'])),
],
],
]) ?>
In another project i was able to make a search box in _view2 by render _search.php inside it and by modifying the ProdutosSearch model. But this was achieved with a GridView that accepts $dataprovider and $searchModel directly inside the widget.
Now in my new project, i'm trying hard to make a search input box to filter what the DetailView shows (it shows the foto, name, category, etc... of products) but cannot do it because the detail view doesn't accept $dataProvider data.
How can i solve this and make a products gallery with search box using a DetailView.
EDIT
Controller action:
public function actionMySearchFunction()
{
$model = new Produtos();
$searchModel = new CategoriasSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$dataProvider = new ActiveDataProvider([
'query' => Produtos::find(),
'pagination' => false,
]);
// get the posts in the current page
$posts = $dataProvider->getModels();
// render
return $this->render('view2', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
'model' => $model,
]);
}
MySearchFunction.php
<?php
public function MySearchFunction()
{
$post = Yii::$app->request->post();
if (!empty($post)){
$postParam1 = $post['categoria']; // get the value ffrom the filter field submitted)
$query = ProdutosSearch::find()->where(['categoria' => $postParam1]);
$dataProvider = new \yii\data\ActiveDataProvider([
'query' => $query,
]);
$dataProvider->pagination->pageSize=15;
return $this->render('view2', [
'dataProvider' => $dataProvider,
]);
}
}
?>
VIEW2
<?php $form = ActiveForm::begin([
'action' => Url::to(['/produtos/my-search-function']),
'method' => 'get',
'options' => ['class' => 'form-inline form-group form-group-sm col-xs-12'],
'fieldConfig' => [
'template' => "{input}",
],
]); ?>
</div>
<?php echo var_dump(Url::to(['/produtos/my-search-function'])); ?>
<!--<nobr><?= $form->field($model, 'nome')->textInput(['placeholder' => 'Nome']) ?>-->
<nobr>
<?= $form->field($searchModel, 'categoria')->dropDownList(ArrayHelper::map(Categorias::find()->all(), 'categoria','categoria'), ['prompt'=>Yii::t('yii', 'Escolha a categoria...')]) ?>
<?= Html::submitButton(Yii::t('app', 'Pesquisar'), ['class' => 'btn btn-warning']) ?>
</nobr>
<?php ActiveForm::end(); ?>
<div class="user-view">
<?= ListView::widget([
'dataProvider' => $dataProvider,
'itemView' => '_view2',
]); ?>
<br/><br/>
</div>
You can easily add the fields you need in an action from section inside the view,
the search button submit the value to an action you use for search and the you can redirect the resulto to the controller you need.
In the action Form you can set the target controller/action. In this way pressing the submit botton the value in the submitted the the controller/action you have eg:
public function actionMySearchFunction()
{
$post = Yii::$app->request->post();
if (!empty($post)){
$postParam1 = $post['name_field_Filter1']; // get the value ffrom the filter field submitted)
$postParam2 = $post['name_field_Filter2'];
$query = YourModel::find()->where(['field1' => $postParam1, 'field2' => $postParam2]);
$dataProvider = new \yii\data\ActiveDataProvider([
'query' => $query,
]);
$dataProvider->pagination->pageSize=15;
return $this->render('your_view', [
'dataProvider' => $dataProvider,
]);
}
}
first add search box on that page :
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
/* #var $form yii\widgets\ActiveForm */
?>
<div class="student-form">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($searchModel, 'nome') ?>
<?= $form->field($searchModel, 'categoria') ?>
<div class="form-group">
<?= Html::submitButton('Apply', ['class' => 'btn btn-success']) ?>
</div>
<?php ActiveForm::end(); ?>
</div>
then in model prepare a filter method.
public function rules()
{
return [
['nome', 'string'],
['categoria', 'string'],
];
}
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
public function search($params)
{
$query = Produtos::find();
$dataProvider = new ActiveDataProvider(['query' => $query]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$query->andFilterWhere(['like', 'nome', $this->nome])
->andFilterWhere(['like', 'categoria', $this->categoria]);
return $dataProvider;
}

Alter a hardcoded form of options based on $user already saved

What I'm trying to accomplish
I'm building a favorites module and I need the ability to:
Select from a dropdown, hardcoded list of options
Have it save to the database
Upon refreshing the page, remove the already saved option from the list of options so it may not be added again
The third part is where I am unsure of how to proceed.
How my code is set up
This is my form:
/*
* Implentation of hook_form().
*/
function f25_favorites_form() {
$listOfPaths = f25_favorites_listOfPaths();
$form['path_options'] = array(
'#type' => 'value',
'#value' => array(
'default' => $listOfPaths['default']['#title'],
'concierge' => $listOfPaths['concierge']['#title'],
'concierge/add' => $listOfPaths['concierge/add']['#title'],
'survey-questions' => $listOfPaths['survey-questions']['#title'],
'survey-questions/add' => $listOfPaths['survey-questions/add']['#title'],
'profiles' => $listOfPaths['profiles']['#title'],
'profiles/add' => $listOfPaths['profiles/add']['#title'],
'statistics' => $listOfPaths['statistics']['#title'],
)
);
$form['path'] = array(
'#type' => 'select',
'#title' => t('Select Page'),
'#required' => TRUE,
'#weight' => '11',
'#options' => $form['path_options']['#value'],
);
$form[submit] = array(
'#type' => 'submit',
'#weight' => '1000000',
'#value' => t('Add')
);
return $form;
}
The name of the paths/options are called via a reference array:
/*
* List of Paths to add to favorites
*/
function f25_favorites_listOfPaths() {
$list = array();
$list = array(
'default' => array(
'#title' => t('Add to favorites'),
),
'concierge' => array(
'#title' => t('Concierge'),
'#image' => drupal_get_path('module', 'f25_favorites').'/img/concierge.png',
'#desc' => t('Concierge'),
),
'concierge/add' => array(
'#title' => t('New Concierge'),
'#image' => drupal_get_path('module', 'f25_favorites').'/img/concierge.png',
'#desc' => t('Concierge > Add'),
),
'survey-questions' => array(
'#title' => t('Survey Questions'),
'#image' => drupal_get_path('module', 'f25_favorites').'/img/survey-questions.png',
'#desc' => t('Current Survey Questions'),
),
'survey-questions/add' => array(
'#title' => t('New Survey Question'),
'#image' => drupal_get_path('module', 'f25_favorites').'/img/survey-questions.png',
'#desc' => t('Survery Question > Add'),
),
'profiles' => array(
'#title' => t('Profiles'),
'#image' => drupal_get_path('module', 'f25_favorites').'/img/profiles.png',
'#desc' => t('User Profiles'),
),
'profiles/add' => array(
'#title' => t('Add Profile'),
'#image' => drupal_get_path('module', 'f25_favorites').'/img/profiles.png',
'#desc' => t('Profiles > Add'),
),
'statistics' => array(
'#title' => t('Statistics'),
'#image' => drupal_get_path('module', 'f25_favorites').'/img/statistics.png',
'#desc' => t('Performance Stats'),
),
);
return $list;
}
And all this is what grabs the data on the databse:
/*
* Write Form data to database
*/
function f25_favorites_form_submit($form, &$form_state){
global $user;
$listOfPaths = f25_favorites_listOfPaths();
$selected = $form_state['values']['path'];
$data = array(
'uid' => $user->uid,
'path' => $selected,
'title' => $listOfPaths[$selected]['#title'],
'weight' => 10,
'timestamp' => time(),
);
drupal_write_record(f25_favorites, $data);
}
Possible Solutions
I've been told that I could used hook_form_alter() in order to modify my array but I am unsure as to when I should be comparing the db_query to my array and how to modify the differences accordingly.
I hope I've done a good job explaining what I'm try to do.
What would be the best way to accomplish this?
Instead of writing every response in f25_favorites_listOfPaths(), shouldn't you get them from the database?
You can then change whatever you want in the submit function to the database so that you don't fetch again the previously selected answer.
Example :
function f25_favorites_listOfPaths() {
return variable_get('f25_favorites_array_' . $user->uid, array(
// your $list array
));
}
function f25_favorites_submit_form($form, &$form_state) {
// your stuff already
drupal_write_record(f25_favorites, $data);
// Now what I propose you to do :)
variable_set('f25_favorites_array_' . $user->uid, array(
// new $list array without the favorite selected
));
}
The use of variable_get/set() should of course be replaced by your own table if you have too much datas.
P.S. : hook_form() does not exist :)

drupal_get_form is not passing along node array

I have not been able to get drupal_get_form to pass on the node data. Code snippets are below. The drupal_get_form documentation (api.drupal.org) states that it will pass on the extra parameters. I am basing the node data not being passed because (apparently) $node['language'] is not defined in hook_form which causes $form['qqq'] not to be created and thus the preview button shows up.
My goal here is that the preview button show up using path "node/add/author" but doesn't show up for "milan/author/add". Any alternative methods for achieving this goal would be helpful but the question I want answered is in the preceding paragraph. Everything I've read indicates that it should work.
This menu item
$items['milan/author/add'] = array(
'title' => 'Add Author',
'page callback' => 'get_author_form',
'access arguments' => array('access content'),
'file' => 'author.pages.inc',
);
calls this code
function get_author_form() {
//return node_form(NULL,NULL);
//return drupal_get_form('author_form');
return author_ajax_form('author');
}
function author_ajax_form($type) {
global $user;
module_load_include('inc', 'node', 'node.pages');
$types = node_get_types();
$type = isset($type) ? str_replace('-', '_', $type) : NULL;
// If a node type has been specified, validate its existence.
if (isset($types[$type]) && node_access('create', $type)) {
// Initialize settings:
$node = array('uid' => $user->uid, 'name' => (isset($user->name) ? $user->name : ''), 'type' => $type, 'language' => 'bbb','bbb' => 'TRUE');
$output = drupal_get_form($type .'_node_form', $node);
}
return $output;
}
And here is the hook_form and hook_form_alter code
function author_form_author_node_form_alter(&$form, &$form_state) {
$form['author']=NULL;
$form['taxonomy']=NULL;
$form['options']=NULL;
$form['menu']=NULL;
$form['comment_settings']=NULL;
$form['files']=NULL;
$form['revision_information']=NULL;
$form['attachments']=NULL;
if($form["qqq"]) {
$form['buttons']['preview']=NULL;
}
}
function author_form(&$node) {
return make_author_form(&$node);
}
function make_author_form(&$node) {
global $user;
$type = node_get_types('type', $node);
$node = author_make_title($node);
drupal_set_breadcrumb(array(l(t('Home'), NULL), l(t($node->title), 'node/' . $node->nid)));
$form['authorset'] = array(
'#type' => 'fieldset',
'#title' => t('Author'),
'#weight' => -50,
'#collapsible' => FALSE,
'#collapsed' => FALSE,
);
$form['author_id'] = array(
'#access' => user_access('create pd_recluse entries'),
'#type' => 'hidden',
'#default_value' => $node->author_id,
'#weight' => -20
);
$form['authorset']['last_name'] = array(
'#type' => 'textfield',
'#title' => t('Last Name'),
'#maxlength' => 60,
'#default_value' => $node->last_name
);
$form['authorset']['first_name'] = array(
'#type' => 'textfield',
'#title' => t('First Name'),
'#maxlength' => 60,
'#default_value' => $node->first_name
);
$form['authorset']['middle_name'] = array(
'#type' => 'textfield',
'#title' => t('Middle Name'),
'#maxlength' => 60,
'#default_value' => $node->middle_name
);
$form['authorset']['suffix_name'] = array(
'#type' => 'textfield',
'#title' => t('Suffix Name'),
'#maxlength' => 14,
'#default_value' => $node->suffix_name
);
$form['authorset']['body_filter']['body'] = array(
'#access' => user_access('create pd_recluse entries'),
'#type' => 'textarea',
'#title' => 'Describe Author',
'#default_value' => $node->body,
'#required' => FALSE,
'#weight' => -19
);
$form['status'] = array(
'#type' => 'hidden',
'#default_value' => '1'
);
$form['promote'] = array(
'#type' => 'hidden',
'#default_value' => '1'
);
$form['name'] = array(
'#type' => 'hidden',
'#default_value' => $user->name
);
$form['format'] = array(
'#type' => 'hidden',
'#default_value' => '1'
);
// NOTE in node_example there is some addition code here not needed for this simple node-type
$thepath='milan/author';
if($_REQUEST["theletter"]) {
$thepath .= "/" . $_REQUEST["theletter"];
}
if($node['language']) {
$thepath='milan/authorajaxclose';
$form['qqq'] = array(
'#type' => 'hidden',
'#default_value' => '1'
);
}
$form['#redirect'] = $thepath;
return $form;
}
That menu path coincides with this theme (PHPTemplate)
This might not be it but I see that you use $node as an object at first (title) and then as an array (to get the language) in the make_author_form() method. If $node is an object, then that explains why you cant retrieve $node['language'].
Not sure if I completely understand what you are trying to do but it would be a good idea to use page arguments for it, I think.
function mymodule_form_alter($form_id, &$form) {
// If $form_id is {node->type}_node_form
// Then, check for the first argument in the URL and hide/show Preview accordingly
}
Turned out to be a basic programming error in line 4 of the make_author_form function. I was zeroing out the $node variable myself.

Resources