how to pass alphanumeric parameter in yii2 controller action - yii2

My code is
class WorkshopsController extends Controller
{
public $layout = false;
public function actionIndex($address)
{
echo $address;
return $this->render('workshops.twig');
}
}
I want to pass any place address to above given action. how can i do that in Yii2. Address can be like "Mazyad Road, Mazyad - Al Ain - أبو ظبي - United Arab Emirates"

You need to request you workshops/index page with address query parameter example.com/workshops/index?address=some address. Yii will automaticaly call your action and pass query params into it.
EDIT
If you wish to use pretty url, you must specify urlManager rules for it. E.g.
rules => [
'workshops/<address>' => 'workshops/index',
],
Also if you wish your page open when addess parameter not specified you can define rules like this:
[
'pattern' => 'workshops/<address>',
'route' => 'workshops/index',
'defaults' => ['address' => ''],
]

Related

Yii2: Replace querystring parameter with value using urlManager

I have a rule in urlManager
'rules' => [
'products/<whatever:[-_0-9a-zA-Z]+>' => 'products/show',
],
and I have controller Products with actionShow()
public function actionShow($name)
{
var_dump($name);
echo 'actionShow is work';
}
But a have an Error: Bad Request (#400) Missing required parameters: name
How to transfer a product's $name to the controller, or how it can be obtained from the URL in the controller?
The CORRECT and COMPLETE answer is that you have to use the rule like below.
'rules' => [
'products/<name:[\-\w]+>' => 'products/show',
],
then in your products controller you don't need to pass $name in the parameter anymore that is why we defined in the rule and you have to use the
Yii::$app->request->queryParams['name']; or Yii::$app->request->get('name') to get the name of the product, your action will look like below
public function actionShow()
{
echo Yii::$app->request->get('name');
}
after doing all the settings above open brower and type
http://yourdomain.com/products/some-product
and it will show you
some-product
When you have to create a link to that page lets say in menu you have under products all products listed and you have to create a link to detail page for all of them i.e actionShow you would do it like below.
<?=Html::a($model->name,
Yii::$app->urlManager->createUrl([
'products/show',
'name'=>$model->slug
])
);
?>
I guess you should specify the name of your variable in the rule. You are calling it whatever, change it to name:
'rules' => [
'products/<name:[-_0-9a-zA-Z]+>' => 'products/show',
],

Yii2: Kartik Select2: Initial Value from Model Attribute

I have a Model who has a column (attribute) that stored a comma separated value of IDs.
For Example,
Movie has a column "Genre" that includes more than one genre, e.g.: 40,20,1,3
How can I use Select2 widget to show these values separated when 'multiple' => true
And how can I save them back into comma-separated value as a string. I want a solution that will allow for quick flexibility. I know you can implode and explode the string but seems too much.
Any help appreciated
If I remember correctly pass the default option as part of the $options configuration for the widget:
echo $form->field($model, 'model_attribute_name')->widget(Select2::className(), [
'data' => $data
'options' => [
'class' => 'form-control',
'placeholder' => 'Choose Option...',
'selected' => 40
],
'pluginOptions' => [
'allowClear' => true,
],
])->label('Select2 Form Field');
This is from memory for grain fo salt here. The documentation at http://demos.krajee.com/widget-details/select2 is not very specific about how to do this.
I don't believe you can do that. Select2 sends the data in post as an array, so you would still need to use implode before saving. What i would do instead is in your model class:
class MyModel extends \yii\db\ActiveRecord {
$public myArrayAttribute;
...
public function beforeSave($insert) {
if (parent::beforeSave($insert)) {
$this->myAttribute = implode(',', $this->myArrayAttribute);
return true;
}
return false;
}
public function afterFind() {
parent::afterFind();
$this->myArrayAttribute = explode(',', $this->myAttribute);
}
}
This way myArrayAttribute will hold the values from the comma separated field as an array. Of course you will need to add validation rules for it and use it instead of your other attribute in create and update forms.
if you're displaying a form with already populated fields, maybe you want to update an already existing object, and you want to display the already saved value for the Select2 field, use 'data' => [ 1 => 'Some value' ], where 1 is the value, associated to the value displayed in the form. You can retrieve stuff to put in data from DB beforehand.
Source: https://github.com/kartik-v/yii2-widget-select2/issues/37

Yii2 use runAction() within beforeAction method

I have a MainController which extends Controller. All my app's controllers extend from MainController which includes various methods and properties which need to be accessible from any Controller.
Within my MainController is beforeAction, which does several things:
Checks for redirects held in the database and performs them if the URL matches one in the DB.
Generates <head> data for each controller
Gets the language and country the user is looking at based on cookie and slug of the URL. (i.e. http://example.com/netherlands).
Will render a generic page from a template if URL matches one from the database's pages table.
It's the last that I am struggling with. In my MainController I have this:
/**
* Before action, check all $this->before_actions satisfy. If no head_data provided, try and fill in some basics
*/
public function beforeAction( $action )
{
// Run parent method first
if (!parent::beforeAction($action))
return false;
// Check redirects
$this->checkRedirects();
if( $this->checkPages() )
{
// If not error page, loop through before methods
if( $action->id !== 'error' )
{
// Loop through actions to peform and do them
foreach ( $this->before_actions as $before_method )
$this->$before_method();
}
return true;
}
}
Where $this->checkPages() contains the following:
/**
* Check for pages
*/
public function checkPages()
{
// Attempt to find page for this request
$page = Page::find()->where( [ 'permalink' => trim( str_replace( getBaseUrl() , "", getCurrentUrl() ), "/" ) ] )->one();
// If found, load it instead
if( !empty( $page ) )
return Yii::$app->runAction( "pages/show", [ 'id' => $page->id ] );
// Else, return
return true;
}
The issue I am having is that if I go to http://example.com/story, because there is no StoryController, the returns a 404 error although the action does run and the view "views/story/show" is output.
How can I prevent this?
EDIT:
To add, the log shows that it first says:
"Unable to resolve the request 'story/index'".
But then additional logs show:
"Route to run: pages/show" ... "Running action: app\controllers\PagesController::actionShow()"
..
Rendering view file: /Users/stefandunn/Documents/Local Machine Development/views/pages/show.php
So I am guessing it's the first log result causing the 404 status
Add one last route that can catch any pattern and redirect to custom action.
'urlManager' => [
'class' => 'yii\web\UrlManager',
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
//...
'<any:.*>' => 'site/index'
],
],

Yii2 Behaviors / Scenarios Modify Attribute

I have a model "Product" that I would like to modify or "mutate" one of its attributes for, but only in specific instances.
I store attribute, price as an integer. So $1.99 gets stored as 199.
I would like to incorporate this with the activeForm in such a way that when getting the price it converts to "1.99" in the field (visually). But when I submit the form, before validation, it modifies the price from "1.99" to "199".
I'm assuming this will require Behaviors, and specifically attaching a behavior to the model before creating the active form. However, I'm still confused on how to set this up. I see there is an AttributeBehavior class or I can make my own Behavior class, but I've been having trouble figuring out implementation in this case.
The situation:
foreach ($store_item->storeProducts as $i=>$product) {
?>
<tr>
<td>
<?= $form->field($product, '['.$i.']price')->label(false); ?>
</td>
</tr>
<?php
$i++;
}
?>
Here is a scenario where I check for empty attribute and assign value before saving. Note owner returns the Model so that you can access model attributes and functions that are public. Let me know if I can explain anything further
public function behaviors()
{
return [
[
'class' => AttributeBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => 'yourAttrib',
],
'value' => function ($event) {
$code = "N/A";
if(!empty($this->owner->yourAttrib))
{
$code = $this->owner->yourAttrib; //here change your attribute accordingly
}
return $code;
},
],
//other behaviors
];
}
You could simply use a getter/setter, e.g. :
public function getRealPrice()
{
return $this->price/100;
}
public function setRealPrice($value)
{
$this->price = $value*100;
}
And don't forget to :
add realPrice in your model's rules,
use realPrice in your form (instead of price).

CakePHP 3 Auth on model other than User

I'm working on a project rebuild using CakePHP, and following the new Authentication documentation here:
http://book.cakephp.org/3.0/en/controllers/components/authentication.html
From what I'm reading, Cake3 uses the userModel='User' by default, but it has the option to set it to whatever you want. In my case, I have all the auth data in the 'Account' model (i.e. userModel => 'Account').
So, in my Account Entity, I added the following code:
protected function _setPassword($password)
{
return (new DefaultPasswordHasher)->hash($password);
}
Additionally, in my accounts table, my 'passwd' field is set to varchar(255) [I've read that's required for some reason].
When I use my default baked 'add' and 'edit' methods, the password is stored in plain text, and not hashed. The ONLY way I've found to get around this is to create a custom method in the AccountsTable class then call it using this kludge:
$this->request->data['passwd'] = $this->Accounts->hashPassword($this->request->data['passwd']);
My Auth component looks like this...
$this->loadComponent('Auth', [
'loginAction' => [
'controller' => 'Accounts',
'action' => 'login'
],
'authError' => 'Unauthorized Access',
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'username',
'password' => 'passwd'
],
'userModel'=>'Accounts'
]
]
]);
Is there a way to do this without dinking around with the raw request data?
Your mutator is named wrongly, the convention for mutators is _set followed by the camel cased field/property name. So since your field name is passwd, not password, it has to be named _setPasswd instead.
protected function _setPasswd($password)
{
return (new DefaultPasswordHasher)->hash($password);
}
See also Cookbook > Entities > Accessors & Mutators