Yii2 Multiple Variables to be passed in URL - url

I want to generate URL's that can handle multiple parameters.
Eg:
www.example.com/product/index?brand=brand-name,
www.example.com/product/index?category=category-name,
I want the url be like :
www.example.com/brand-name,
www.example.com/category-name
Tried some url rules,but it doesn't work.
'rules' => [
[
'pattern' => '<brand:\w+(-\w+)*>/<category:\w+(-\w+)*>',
'route' => 'product/index',
'defaults' => [
'brand' => null,
'category' => null,
]
]
]
This is my reference :
Reference question

To do this you will have to stick to the prefixed version. So the brand param should always be prefixed with brand- and the category always with category-. Otherwise there is no way to tell what is what.
Add the following rules. This will put everything that matches brand-\w+ in the brand argument and pass it to product/index. Same for category.
'<brand:brand-\w+>' => 'product/index',
'<category:category-\w+>' => 'product/index',
To see that it works
public function actionIndex($brand = null, $category = null) {
echo "Brand: $brand<br />";
echo "Category: $category<br />";
echo Url::toRoute(['dev/index', 'brand' => 'brand-name']) . '<br />';
echo Url::toRoute(['dev/index', 'category' => 'category-name']) . '<br />';
}

Related

How to fix 'The input was not found in the haystack' in ZF2?

I have an issue in my code which I can't resolve. I'm using Zend framework 2.4 and I wrote a form but as soon as I validate it, I got the error The input was not found in the haystack. This are the 3 input where I got the error:
$this->add(array(
'name' => 'ACTIVITE1',
'type' => 'Zend\Form\Element\Select',
'required' => 'required',
'options' => array(
'value_options' => array(
'Choisir l\'activité'
),
'disable_inarray_validator' => false,
),
'attributes' => array(
'class' => 'form-control',
'id' => 'select-session1'
)
));
$this->add(array(
'name' => 'ACTIVITE2',
'type' => 'Zend\Form\Element\Select',
'required' => 'required',
'options' => array(
'value_options' => array(
'Choisir l\'activité'
)
),
'disable_inarray_validator' => false,
'attributes' => array(
'class' => 'form-control',
'id' => 'select-session2'
)
));
$this->add(array(
'name' => 'ACTIVITE3',
'type' => 'Zend\Form\Element\Select',
'required' => 'required',
'options' => array(
'value_options' => array(
'Choisir l\'activité'
)
),
'disable_inarray_validator' => false,
'attributes' => array(
'class' => 'form-control',
'id' => 'select-session3'
)
));
I saw in other form that I should put 'disable_inarray_validator' => false but this doesn't work too.
Of course it's not working.
That message comes from the \Zend\Validator\InArray and essentially, it means: "your user is doing something hacky with your select, pay attention".
An exemple would be a Select Preferred fruit with two options, like "Banana" and "Ananas", but the user "hacks" the select and sends to the server the value "Audi". The InArray validator is really important and shouldn't be disabled (well, only in a few exceptions..).
Now, why are you getting this error? The answer is... You didn't told what are the select options. You created a Select, but you didn't specified what are its options. You put the label/placeholder at the place of the options. A correct Select would be:
$this->add(array(
'name' => 'ACTIVITE1',
'type' => 'Zend\Form\Element\Select',
'required' => 'required',
'options' => array(
'value_options' => array(
1 => 'Fitness',
2 => 'Parcour',
3 => 'Velo',
4 => 'Tapis roulant',
// ... and so on
)
),
'attributes' => array(
'class' => 'form-control',
'id' => 'select-session1',
'placeholder' => "Choisir l'activité"
)
));
What is "weird" is the fact that you are filling something in an empty select, and my question then is: why?
Selects are (typically) there for a predefined list of values. If you want to allow your users to fill a custom text, then you should consider to create a Text field with the autocomplete option.
Edit: select with values from DB
If you want to create a select with a list of options that come from the database, the route is a bit more complex, but once you've learned how to do it, it will become way easier.
PAY ATTENTION: this will not be a "copy&paste solution". Since I'm not having access to your code, I'm making up names (classes, namespaces, methods, variables) just to create a complete example :)
First off, you must create a custom element. In this case, it will be a custom select:
namespace Yournamespace;
use Zend\Form\Element\Select;
use Yournamespace\ActivityMapper;
class ActivitySelect extends Select {
protected $activityMapper;
public function __construct(ActivityMapper $activityMapper, $name = null, $options = []) {
parent::__construct($name, $options);
$this->activityMapper = $activityMapper;
}
public function init() {
$valueOptions = [];
foreach ($this->activityMapper->fetchAll() as $activity) {
$valueOptions[$activity->getActivityId()] = $activity->getActivityName();
}
$this->setValueOptions($valueOptions);
}
}
What is really important here is that you must instantiate your element (options, classes, and so on..) inside init method.
Since this element has a dependency (ActivityMapper), you'll have to create a factory for this element:
namespace Yournamespace;
use Zend\ServiceManager\Factory\FactoryInterface;
use \Interop\Container\ContainerInterface;
class ActivitySelectFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null): object {
$activityMapper = $container->get(\Yournamespace\ActivityMapper::class);
return \Yournamespace\ActivitySelect($activityMapper);
}
}
This factory must be added in the configuration, more precisely, inside module.config.php:
return [
// ...
'form_elements' => [
'factories' => [
\Yournamespace\ActivitySelect::class => \Yournamespace\ActivitySelectFactory::class,
]
]
// ...
];
Now, you must modify your form too. All elements must be added to form inside init method, and not inside the constructor:
namespace Yournamespace;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
class ActivityForm extends Form implements InputFilterProviderInterface {
public function init() {
parent::init();
// ...
$this->add([
'name' => 'ACTIVITE1',
'type' => \Yournamespace\ActivitySelect::class,
'attributes' => [
'class' => 'form-control',
'id' => 'select-session1',
'required' => true, // This will work only clientside, don't forget the inputfilter!
'placeholder' => 'Choisir l\'activité',
],
'options' => [
'label' => 'Choisir l\'activité',
]
]);
// ...
}
public function getInputFilterSpecification() {
// ...
$inputFilter[] = [
'name' => 'ACTIVITE1',
'required' => true
];
// ...
return $inputFilter;
}
}
Finally, you'll have to modify your controller too, because you need to retrieve the form from the FormElementManager:
namespace Yournamespace;
use Zend\Form\FormElementManager;
class YourController extends AbstractActionController {
private $formManager;
public function __construct(FormElementManager $formManager) {
$this->formManager = $formManager;
}
public function choisirActiviteAction(){
// ...
$form = $this->formManager->get(\Yournamespace\ActivityForm::class);
// ...
}
}
A next nice step would be to create a controller plugin for the $formManager, instead of making it as a dependency of each controller, but this is a different problem..
thanks for your answer! I understand what you mean but i did this because i don't want to control the select because i can't control all the possible options.
Because the options are from a database, i get them doing an AJAX request and i add all the option to the select in the view. So the options can vary and are not fixed.
Here is my AJAX request :
$("#input-dtNaissance").change(function() {
$.when($(".activites-options").remove()).then(function() {
if ($("#input-dtNaissance").val() != " ") {
//on supprime les anciennes activités proposées au cas où il y en ai
var year = $("#input-dtNaissance").val().substring(0,4);
if (year == "") {
year = 1;
}
// si date supprimée/réinitialisée
if (year == 1) {
// on supprime les activités proposées du select
$(".activites-options").remove();
} else {
$.getJSON("http://localhost/ecoleMunicipalSport/app/public/activite/listActivitySession1/" + year, function(data) {
data.forEach(element => {
var option = "<option value='" + element.ID_ACTIVITES + "' class='activites-options'>" + element.activite[0] + element.activite.substring(1).toLowerCase() + " avec " + element.INTERVENANT + " - " + element.HORAIREDEB + "/" + element.HORAIREFIN + " (" + element.site[0] + element.site.substring(1).toLowerCase() + ")" + "</option>";
$("#select-session1").append(option);
});
});
$.getJSON("http://localhost/ecoleMunicipalSport/app/public/activite/listActivitySession2/" + year, function(data) {
data.forEach(element => {
var option = "<option value='" + element.ID_ACTIVITES + "'class='activites-options'>" + element.activite[0] + element.activite.substring(1).toLowerCase() + " avec " + element.INTERVENANT + " - " + element.HORAIREDEB + "/" + element.HORAIREFIN + " (" + element.site[0] + element.site.substring(1).toLowerCase() + ")" + "</option>";
$("#select-session2").append(option);
});
});
$.getJSON("http://localhost/ecoleMunicipalSport/app/public/activite/listActivitySession3/" + year, function(data) {
data.forEach(element => {
var option = "<option value='" + element.ID_ACTIVITES + "' class='activites-options'>" + element.activite[0] + element.activite.substring(1).toLowerCase() + " avec " + element.INTERVENANT + " - " + element.HORAIREDEB + "/" + element.HORAIREFIN + " (" + element.site[0] + element.site.substring(1).toLowerCase() + ")" + "</option>";
$("#select-session3").append(option);
});
});
}
}
});
})
I know that it's not very safety and if you have a better solution with more security i'm open to the propositions.
Good evening.
PS : Sorree faur my terribl anglish :)

Yii2: GridView: add button or link to controller action

I have a controller with an action method:
namespace frontend\controllers;
class EmployeeController extends FrontController
{
/**
* Deletes an existing Employee status.
* #param integer $id
* #return mixed
*/
public function actionDeleteStatus($status_id)
{
error_log("actionDeleteStatus " . $status_id);
return $this->redirect(['update']);
}
}
In update form, I have a detail GridView, in which I want to add a "delete" link with an URL for this method as a GET request.
I try to get the URL with this: Url::toRoute(['employee/deleteStatus','status_id' => $model->status_id]) which gives me an url like /employee/deleteStatus?status_id=4 and throws a 404, here is the detailed code:
<div class="col-xs-12">
<?php
echo Html::label(Yii::t('app', 'Employee status history'));
echo GridView::widget([
'summary' => '',
'options' => [
'id' => 'status-history',
],
'emptyText' => '',
'export' => false,
'dataProvider' => $statusHistory,
'columns' => [
[...],
[
'class' => 'kartik\grid\DataColumn',
'attribute' => 'status_id',
'headerOptions' => [ 'class' => 'kv-grid-hide' ],
'contentOptions' => [ 'class' => 'kv-grid-hide' ]
],
[
'class' => 'yii\grid\ActionColumn',
'urlCreator' => function($action, $model, $key, $index) {
return Url::toRoute(['employee/deleteStatus','status_id' => $model->status_id]);
},
'template' => '{delete}',
'contentOptions' => ['class' => 'column-action'],
'buttons' => [
'delete' => function ($url, $model, $key) {
if (Yii::$app->user->can('globalDAF')) {
$options = [
'title' => Yii::t('app', 'Delete'),
'aria-label' => Yii::t('app', 'Delete'),
'data-confirm' => Yii::t('app', 'Sure to delete status?'),
'data-method' => 'post',
'data-pjax' => '0',
'class' => 'btn-llyc'
];
return Html::a('<span class="glyphicon glyphicon-remove"></span>', $url, $options);
} else {
return;
}
}
]
]
],
'hover' => true,
'responsiveWrap' => false
]);
?>
</div>
Is the url generation wrong? Why am I getting a 404?
Thanks.
For example, index becomes actionIndex, and hello-world becomes
actionHelloWorld.
Note: The names of the action methods are case-sensitive. If you have
a method named ActionIndex, it will not be considered as an action
method, and as a result, the request for the index action will result
in an exception. Also note that action methods must be public. A
private or protected method does NOT define an inline action.
Link
Url::toRoute(['employee/delete-status','status_id' => $model->status_id])
Or in config file:
'urlManager' => [
'class' => 'yii\web\UrlManager',
#code ..
'rules' => [
'employee/deleteStatus' => 'employee/delete-status',
],
],

Yii2: Sending params with Anchor as POST request

I try to get POST Variables in my controller from view with:
<?= GridView::widget([
'dataProvider' => $dataProvider_products,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
[
'label'=>'Name',
'format' => 'raw',
'value'=>function ($data) use ($model, $dataProvider_products) {
return Html::a($data['name'],['suppliers_orders/addproduct', 'order' => $model->id, 'product' => $data['id'],
'data-method'=> 'post',
'data-params' => ['dataProvider' => $dataProvider_products],
]);
},
],
'supplier_product_number',
'size',
'price_net',
],
]); ?>
The parameter dataProvider will always be sent with the URL and results in a GET variable. What is wrong, respectively what must be done, that dataProvider will be sent as POST variable?
Part of my controller is:
public function actionAddproduct($order, $product){
// look for GET variable
$request = Yii::$app->request->get('data');
$dataProvider = $request['params']['dataProvider'];
// look for POST variable
$param1 = Yii::$app->request->post('dataProvider', null);
$dataProvider_suppliers_orders_products = $dataProvider;
return $this->actionView($order);
}
First of all you are passing the data-params in the url parameter rather than setting in the options parameter, so yes it will always be sent as query string no matter you pull your hairs off and become bald ¯\_(ツ)_/¯.
Then According to the DOCS
If the attribute is a data attribute as listed in
yii\helpers\Html::$dataAttributes, such as data or ng, a list of
attributes will be rendered, one for each element in the value array.
For example, 'data' => ['id' => 1, 'name' => 'yii'] generates
data-id="1" data-name="yii" and 'data' => ['params' => ['id' => 1,
'name' => 'yii'], 'status' => 'ok'] generates
data-params='{"id":1,"name":"yii"}' data-status="ok"
So, you need to change the anchor to look like
Html::a($data['name'], ['suppliers_orders/addproduct', 'order' => $model->id, 'product' => $data['id']], [
'data' => [
'method' => 'POST',
'params' => ['dataProvider' => $dataProvider_products]
]
]);
But since you are passing the $dataProvider object to the params it aint going to work because it will be changed to [Object Object] but if it is simple text then it will work, otherwise you have to change your approach.
Your complete code for the GridView should look like below
<?=
GridView::widget([
'dataProvider' => $dataProvider_products,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
[
'label' => 'Name',
'format' => 'raw',
'value' => function ($data) use ($model, $dataProvider_products) {
return Html::a($data['name'], ['suppliers_orders/addproduct', 'order' => $model->id, 'product' => $data['id']], [
'data' => [
'method' => 'POST',
'params' => ['dataProvider' => $dataProvider_products]
]
]);
},
],
'supplier_product_number',
'size',
'price_net',
],
]);
?>

Yii 2 GridView Link Not Working

I have this code in my index.php in my view:
<p>
<?= Html::a('Create Invoice', ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
['class' => 'yii\grid\SerialColumn'],
//'inv_id',
'cust_name',
'currency',
'inv_date',
'inv_duedate',
'prod_name',
//'prod_desc',
//'prod_quanity',
'prod_price',
//'prod_tax',
//'amount',
//'subtotal',
'total',
[
'attribute' => 'image',
'format' => 'raw',
'value' => function($data){
//return Html::a($data->image, $data->image, $data->image);
return Html::a(Html::encode($data->image),$data->image);
//return Html::a($data->image, $data->image, array('target' => '_blank'));
//return Html::a(Html::encode('file'),'invoice/index');
}
],
//'poso_num',
//'subheading',
//'footer',
//'memo',
['class' => 'yii\grid\ActionColumn'],
],
]); ?>
I have already displayed the link/path of a specific file, when I click it, nothing happens. When I hover it, I can see the link, example: file:///C:/wamp3/www/basicaccounting/web/pdf/attachment.pdf, in the status bar (lower left corner of the page). I also tried right click + Open in New Tab, the url is just about:blank.
I also tried each of those commented return statements, still the same results.
Any thoughts about this?
Edit:
My problem is with my file path i.e. file:///C:/wamp3/www/basicaccounting/web/pdf/attachment.pdf
My path in the link needs to be relative to the document root i.e. /basicaccounting/web/pdf/attachment.pdf, and not in C drive.
So I tried:
'value' => function($data){
$basepath = str_replace('\\', '/', Yii::$app->basePath).'/web/';
$path = str_replace($basepath, '', $data->file);
return Html::a($data->file, $path, array('target'=>'_blank'));
}
Now it works fine.
I think I have solved my own problem.
My problem is with my file path i.e. file:///C:/wamp3/www/basicaccounting/web/pdf/attachment.pdf
My path in the link needs to be relative to the document root i.e. /basicaccounting/web/pdf/attachment.pdf, and not in C drive.
So I tried:
'value' => function($data){
$basepath = str_replace('\\', '/', Yii::$app->basePath).'/web/';
$path = str_replace($basepath, '', $data->file);
return Html::a($data->file, $path, array('target'=>'_blank'));
Now it works fine. Thanks everyone!

Zend Framework: create url to other site

I am new with Zend and i don't know how to generate url from text, eg. 'example.com' should be 'http://example.com/'. How to simply do this?
There is url and a serverUrl helpers. See initial helpers. Or you can write a custom helper.
serverUrl helper
serverUrl($requestUri = null)
Helper for returning the current server URL (optionally with request URI).
// Current server URL in the example is: http://www.example.com/foo.html
echo $this->serverUrl();
// Output: http://www.example.com
echo $this->serverUrl(true);
// Output: http://www.example.com/foo.html
echo $this->serverUrl('/foo/bar');
// Output: http://www.example.com/foo/bar
echo $this->serverUrl()->getHost();
// Output: www.example.com
echo $this->serverUrl()->getScheme();
// Output: http
$this->serverUrl()->setHost('www.foo.com');
$this->serverUrl()->setScheme('https');
echo $this->serverUrl();
// Output: https://www.foo.com
Url helper
url($urlOptions, $name, $reset, $encode)
Creates a URL string based on a named route. $urlOptions should be an associative array of key/value pairs used by the particular route.
// Using without options: (current request is: user/id/1)
echo $this->url();
// Output: user/info/id/1
// Set URL options:
echo $this->url(
array('controller' => 'user', 'action' => 'info', 'username' => 'foobar')
);
// Output: user/info/username/foobar
// Using a route:
$router->addRoute(
'user',
new Zend_Controller_Router_Route(
'user/:username',
array(
'controller' => 'user',
'action' => 'info',
)
)
);
echo $this->url(array('name' => 'foobar'), 'user');
// Output: user/foobar
// Using reset: (current request is: user/id/1)
echo $this->url(array('controller' => 'user', 'action' => 'info'), null, false);
// Output: user/info/id/1
echo $this->url(array('controller' => 'user', 'action' => 'info'), null, true);
// Output: user/info
// Using encode:
echo $this->url(
array('controller' => 'user', 'action' => 'info', 'username' => 'John Doe'), null, true, false
);
// Output: user/info/username/John Doe
echo $this->url(
array('controller' => 'user', 'action' => 'info', 'username' => 'John Doe'), null, true, false
);
// Output: user/info/username/John+Doe

Resources