Yii2 - DetailView Search box - search

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;
}

Related

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 how to change text colour of navbar

I want to ask you how to change a colour of text in navbar (where is the Home, About,...) in Yii2 framework basic template? I've tried so many things but nothing worked. Thank you for reply!
In the case of the text of the <a> element (the link ) of navbar the color is defined inside the bootstrap.min.css.
.navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus {
background-color: #1a242f;
color: #ffffff;
}
Then if you want to change the css then you must change the bootstrap.css and the minimize ..
A simple way is change directly the style in the layout ..
changing directly the style of the item using options for the li tag and linkOption for the related link
<?php
NavBar::begin([
'brandLabel' => 'Name',
'brandUrl' => Yii::$app->homeUrl,
'options' => [
'class' => 'my-navbar navbar-fixed-top',
],
]);
echo Nav::widget([
'options' => ['class' => 'navbar-nav navbar-right'],
'items' => [
['label' => 'Home', 'url' => ['/site/index'], 'options' => ['style' => 'background-color: #F00;']],
['label' => 'About', 'url' => ['/site/about']], 'linkOptions' => ['style' => 'color: #000;']],
Yii::$app->user->isGuest ?
['label' => 'Login', 'url' => ['/site/login']] :
[
'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
'url' => ['/site/logout'],
'linkOptions' => ['data-method' => 'post']
],
],
]);
NavBar::end();
?>
Not
'options' => ['style' => 'color: #000;']]
but
'linkOptions' => ['style' => 'color: #000;']]
linkOptions works! Color changed
Instead of using 'linkOptions' => ['style' => 'color: #000;']]
I did 'linkOptions' => ['class' => 'myCssClass']]
This worked for me

Yii2 Modify find() Method in Model 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
]);

Checkbox column with Kendo grid

I wanted to add a checkbox column as first column to below grid.
Can some one help me how to add it?
#(Html.Kendo().Grid(Model)
.Name("items")
.Columns(columns =>
{
columns.Bound(p => p.itemname).Title("Name");
columns.Bound(p => p.cost).Title("Cost");
columns.Bound(p => p.stockinhand).Title("Stock in hand");
columns.Command(command => command.Destroy()).Width(100);
})
.Pageable()
.DataSource(dataSource => dataSource
.Server()
.Model(model => model.Id(p=>p.Id))
.Destroy(update => update.Action("EditingInline_Destroy", "Grid"))
)
)
This is how I did it:
columns.Template(#<text></text>).ClientTemplate("<input type='checkbox' #= IsAdmin ? checked='checked':'' # class='chkbx' />")
and then on javascript:
$(function () {
$('#grid').on('click', '.chkbx', function () {
var checked = $(this).is(':checked');
var grid = $('#grid').data().kendoGrid;
var dataItem = grid.dataItem($(this).closest('tr'));
dataItem.set('IsAdmin', checked);
})
})
Hi you can add Checkbox in Header and Column like below :
columns.Bound(p => p.Status).HeaderTemplate("<input id='selectall' class='chkbx' type='checkbox' onclick='ToggleChkBox(this.checked);' />").ClientTemplate("<input id='checkbox' onclick='grdChkBoxClick(this); ' class='chkbxq' type='checkbox' />").Sortable(false).Filterable(false).Width(30);
And FInd Checkbox click like below :
//Cell click Checkbox select
$('#Grid').on("click", "td", function (e) {
var selectedTd = $(e.target).closest("td");
var grdChkBox = selectedTd.parents('tr').find("td:first").next("td").find('input:checkbox');
grdChkBox.prop('checked', !grdChkBox.prop('checked'));
});
And do Check all checkbox functionality like below :
function ToggleChkBox(flag) {
$('.chkbxq').each(function () {
$(this).attr('checked', flag);
});
}
I normally add a boolean column in the model; like following.
#(Html.Kendo().Grid(Model)
.Name("items")
.Columns(columns =>
{
columns.Bound(p => p.status).ClientTemplate("<input type='checkbox' disabled #= status == true ? checked='checked' : '' # />");
columns.Bound(p => p.itemname).Title("Name");
columns.Bound(p => p.cost).Title("Cost");
columns.Bound(p => p.stockinhand).Title("Stock in hand");
columns.Command(command => command.Destroy()).Width(100);
})
.Pageable()
.DataSource(dataSource => dataSource
.Server()
.Model(model => model.Id(p=>p.Id))
.Destroy(update => update.Action("EditingInline_Destroy", "Grid"))
)
)
And to disable it until you press "Edit" button just add "disabled" in the ClientTemplate. That should do it. Thanks.
you can add checkbox in each row with header using this,
#(Html.Kendo().Grid<Kendo.Mvc.Examples.Models.ProductViewModel>()
.Name("Grid")
.Columns(columns => {
columns.Select();
columns.Bound(p => p.ProductName);
columns.Bound(p => p.UnitPrice);
columns.Bound(p => p.UnitsInStock);
columns.Bound(p => p.Discontinued);
})
.Pageable()
.Sortable()
.Events(ev=>ev.Change("onChange"))
.PersistSelection()
.DataSource(dataSource => dataSource
.Ajax()
.Model(model => model.Id(p => p.ProductID))
.Read(read => read.Action("Selection_Read", "Grid"))
))
Here we use PersistSelection() for persistting selected items across all pages.
If column.Select() gives error or not bind the grid then upgrade your kendo UI version. It will work.

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