Yii. Change URL based on model parameter value - yii

I would like to use the same model "Work" for handling similar CRUD operations for "Services" and "Markets" pages. There is a column in the Work mysql table called "category". If category is "service", then I would like the Index to show list of Services that are stored in the Work table. Similarly for the "Markets" page.
I would need two urls for the same Model (in the menu and create/ update operations etc.). How can I set this up in the URL Manager?
'services/create/' => 'work/create?&category="services"',

You would do it like this:
array(
//put it first so it has highest priority
'services/create' => 'work/create/category/services',
//other rules follow
)
Then your function in your controller would look like this:
public function actionCreate($category) {
//your code
}

Related

Create a ActiveDataProvider based on ActiveRecord Relation in Yii2

I have a many-to-many relation setup using a junction table in MySQL. Table Article is related to Activity via table Article_Activity.
In model Article I have a relation setup like this
public function getActivities()
{
return $this->hasMany(Activity::className(), ['id' => 'activity_id'])
->viaTable('article_activity', ['article_id' => 'id']);
}
When rendering a view for one Article I would like to display a GridView of all Activities related to that Article.
The way most people seem to this is to create a ActiveDataProvider and insert a query into it that fetches related data but that feel a bit redundant since I have the relation setup in the model and there should be a way to get a dataprovider from that.
My question is: Is there a way to get a yii\data\ActiveDataProvider or yii\db\Query based on a instantiated models relation that can be used to display all related records in a GridView?
You can actually call it like this:
$dataProvider = new ActiveDataProvider([
'query' => $article->getActivities(),
]);
If you call the get method directly you get a yii\db\ActiveQueryInterface which is what you need to provide as query to the ActiveDataProvider.
When you call the activities attribute like $article->activities the ActiveQueryInterface is executed and you get the records from the query results.
Based on you Article model you have the activities relation that get multiple Activity related to you Article
a normal dataProvider like
$dataProvider = new ActiveDataProvider([
'query' => Article::find()->joinWith(['activities'])->
where(['your_column'=> $your_value]),
]);
return activeRecord whit also the related activities
you can refer to the related models inside the dataProvider
$models = $dataProvider->models; // this is a collection of model related to the dataProvider query
// so the firts model is $model=models[0]
then for each of this you can obtain acyivities
$activities = $model->activities;
or value
$my_activity_field = $model->activities->my_activity_field

How to pass a parameter to a scenario in Yii?

How to pass $id to search scenario? Maybe in model look like this, so I can call like in controller like:
$model = new job('search',$id);
I think that you are trying to do a search. Search is one thing, a "scenario" is something else.
Scenarios are used in validation rules in order to be able to validate the same model in multiple ways depending from where you're inserting/adding OR searching data.
There's also a scenario called 'search' that is used by the model's search() method, but I tell you why:
There are a couple of ways to search for something in your database using Yii, I will mention two:
1) By using ClassName::model()->findCommandHere
And there are a couple of them:
ClassName::model()->findByPk($id);
ClassName::model()->findAll("id=$id");
ClassName::model()->findByAttributes(array('id'=>$id));
And so on, more here: http://www.yiiframework.com/doc/guide/1.1/en/database.ar#reading-record
2) By using the model's search() method
This way of finding data is mostly used for easily creating search pages and in combination with data grids.
If you generate CRUD code with the GII code generation tool it will generate all the parts for you, but I will explain each part how it works.
This code is from the blog demo found in Yii files:
In controller it defines a $model using Post class and 'search' as scenario.
$model=new Post('search');
if(isset($_GET['Post'])) // <- checks if there are search params in the URL
$model->attributes=$_GET['Post']; // <- assigns all search params masively to the model (later you'll see why)
$this->render('admin',array(
'model'=>$model,
));
The 'search' scenario here tells Yii what validation rules to use when assigning search parameters directly from $_GET (URL).
You can see that the params are assigned massively to reduce code written but $model->attributes=$_GET['Post'] it is the same as doing:
$model->title=$_GET['Post']['title'];
$model->status=$_GET['Post']['status'];
In the Post model you can find the validation rules for the search scenario. Tells Yii that it is safe to assign title and status fields in order to later use them in the search.
public function rules()
{
return array(
// ... //
array('title, status', 'safe', 'on'=>'search'),
);
}
Then also in the Post model you can see the search() method that will actually be used to get the data:
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('title',$this->title,true);
$criteria->compare('status',$this->status);
return new CActiveDataProvider('Post', array(
'criteria'=>$criteria,
'sort'=>array(
'defaultOrder'=>'status, update_time DESC',
),
));
}
The search method creates a "criteria" and applies the desired way of filtering using the values you have previously assigned to this model. See the $this->title it comes from the $model->attributes=$_GET['Post'] you used in the controller.
The criteria can be used directly on the model, such as Post::model()->findAll($criteria), but in this case the search() method uses something different, a "data provider".
The data provider is a good thing because it provides you a lot of tools in one place, it returns you the data, but also the pagination, and the sorting, so you don't have to manually define more code for that purposes (CPagination, CSort).
Finally, in the view admin.php in this case it will display the results using a grid view:
$this->widget('zii.widgets.grid.CGridView', array(
'dataProvider'=>$model->search(),
'filter'=>$model,
'columns'=>array(
array(
'name'=>'title',
'type'=>'raw',
'value'=>'CHtml::link(CHtml::encode($data->title), $data->url)'
),
array(
'name'=>'status',
'value'=>'Lookup::item("PostStatus",$data->status)',
'filter'=>Lookup::items('PostStatus'),
),
),
));
Now you can see that in the configuration of the grid it passes $model->search() method as the dataProvider that the grid should use.
Now the grid can access the rest of the dataProvider elements such as sort, pagination and display them on the page.
If you did not want to use the CGridView because it's a very basic table and you want to create your own html markup, you can also retrieve the dataProvider and its components one by one and place them in your HTML code and display data as you want:
$dataProvider=$model->search(); // get the dataprovider from search method
$models=$dataProvider->getData(); // actually get the data (rows) and assign them to a $models variable that you can put in a foreach loop
// show pagination somewhere
$this->widget('CLinkPager', array(
'pages' => $dataProvider->pagination,
));
// create sort links
echo $dataProvider->sort->link('title', 'Title');
So I hope it solves some of your doubts on how to use Yii for displaying/searching data.
I suggest you read the official manual: http://www.yiiframework.com/doc/guide/1.1/en/index
I also suggest to look at the API and so search there all the Yii components to see what methods and params they have: http://www.yiiframework.com/doc/api/
Also exploring the framework codebase manually is quite a good way to learn. If you don't know how CActiveDataProvider works, then find the CActiveDataProvider class file in the code and you'll see all the methods and properties that it uses, so do this for everything you don't understand how it works.
Also for beginners I recommend using a good IDE that auto-completes code and allows you to Ctrl+Click a class name and it will locate the original file where it was defined. I use NetBeans for PHP and after creating a project I add Yii framework files to the project's include paths that way NetBeans knows how to find the framework files for auto-complete and for ctrl+click.
Yes you can define scenario with parameter but that is included within the class constructor of the model
$model = new Job('search'); // creating a model with scenario search
If you wish to include more parameters, then you need to use createComponent -remember, all is a component
$model = Yii::createComponent(array('class'=>'Job','scenario'=>'search'));
I think this will simply do the job without needing any scenario
$model = new job;
$model->search($id);
But If I have failed to understand your problem then you can also try this
$model = new job('search');
$model->search($id);
Think of scenarios as a special variable that you can use in the model.
$userModel = new User("register");
$userModel->setId = 10;
which is the same
$userModel = new User();
$userModel->scenario = 10
$userModel->setId = 10;
And in your model
class Manufacturer extends CActiveRecord
{
// :
if ($this->scenario == 'register') ...
// :
}

yii cGridView multicomment

I'm not sure what is the way to do this , so here I ask:
I have a Person model and Event model, and a connection table Person_Event.
The interface that I got now works in the following way:
A person is logging in and his id is being send via URL
The person is selecting events he is interested in from the cGridView (checkbox column)
Writing some comment
4.Pressing send button , and the following create action is being triggered:
public function actionXcreate()
{
$model=new Person_Event;
if(isset($_POST['Person_Event']))
{
foreach ($_POST['selectedIds'] as $eventId)
{
$pmodel=new Person_Event;
$pmodel->person_id=$this->_person->id; //the id of the person who is logged in
$pmodel->attributes=$_POST['Person_Event']; //the comment
$pmodel->event_id = $eventId; //all the events he checked in the grid
if (!$pmodel->save()) print_r($pmodel->errors);
}
$this->redirect(array('site/success'));
}
So far , all is logical and simple. However , what I end up is that the comment the person wrote is being duplicated to every person_event row in the DB.
I want to put a text box in each row of the grid , and the commnet that will be written there will go to the specific event.
Now , I found this topic in yii about "admin-panel"
which is kind of helpful , BUT:
I already have a foreach in the action , the one that matches the person's id with the event's id , so how can I put another individual comment for each combo?
The default CGridView supports only basic functionality, you would need to extend CGridView or use an extension to make columns editable
Easiest way to do this is use something like TbEditableColumn from Yii-booster library
see http://yiibooster.clevertech.biz/extendedGridView#gridcolumns EditableColumn in the additional column types section
If you do not like or wish to use twitter-bootstrap styling a standalone extension like http://www.yiiframework.com/extension/eeditable will help.
Alternatively you can extend CGridView yourself to extend it to support column level editing

Laravel dynamic route from MySQL

I am trying to generate routes from one template "tuning.blade.php"
I have a DB with 250 rows I would like to dynamically create 250 routes with one route or one controller.
I want to be able to use these URLs
laravel.dev/tuning/(field from DB row 1)
laravel.dev/tuning/(field from DB row 2 and so on)
I want to put the DB requests in tuning.blade.php so that this template can display all 250 rows using 250 different URLS
I have tried to use the first example from Laravel Docs
class UserController extends BaseController {
/**
* Show the profile for the given user.
*/
public function showProfile($id)
{
$user = User::find($id);
return View::make('user.profile', array('user' => $user));
}
}
Route::get('user/{id}', 'UserController#showProfile');
I also got some interesting search results from http://forumsarchive.laravel.io/viewtopic.php?id=9010
But alsas I always end up with a notfound exception
But I am unsure what to put in my tuning template to display anything at all. my Tuning template reside in app/views/home/tuning.blade.php
Currently I have got the error "Symfony \ Component \ HttpKernel \ Exception \ NotFoundHttpException"
Can anyone put me in the right direction of where I can find a resource to help me understand?
You said you want to be able to use these URLs:
laravel.dev/tuning/(field from DB row 1)
laravel.dev/tuning/(field from DB row 2 and so on)
You can do this by declaring a route like this:
Route::any('/tuning/{field}', 'TuningController#someMethod'); you may get/post
You shouldn't run sql queries from your view and if you want to really declare some dynamic routes for each fields from your database then you may do id right from your routes.php file, for example, assume that you have a table named tunings and that table contains some fields including id, name and some others. Now, to declare routes individually for each routes dynamically using the tuning field of table tunings you may create a method in your TuningController, something like this:
class TuningController extends baseController {
// other methods...
public function registerTuningRoutes()
{
$tunings = Tuning::all(); // Assume that you have a model Tuning
// Or you may use this instead
$tunings = DB::table('tuning')->get();
// Now loop all tunings and declare routes
foreach($tunings as $tuning) {
$url = '/tuning/' . $tuning->name;
$route_name = 'tuning.' . $tuning->name;
Route::any($url, $route_name); // You may use get/post
}
}
public function TuningMethod($tuning = null)
{
// $tuning will contain the current tuning name, check
dd($tuning);
}
}
Now in your routes.php file use something like this:
Registers route for each tuning name in database
App::make('TuningController')->registerTuningRoutes();
From your terminal/command prompt check the routes by running following command:
php artisan routes
But, I think, you don't need to do this, only one route is enough as I mentioned earlier in my answer.

Yii - one controller multiple urls

I'm trying to create two Yii models that are very similar and can share the same database table. One is a 'question' and one is an 'article'. They both share a title, body, and author. 'Question' has an additional field in the table that 'article' does not need to interact with called follow_up.
Most methods and validation is the same, but there are some tiny differences that could easily be done with if statements. The main problem I'm seeing is the URL, I want separate URLs like site.com/question and site.com/article but have them both interact with the same model, controller, and views.
How can this be done?
Use the urlManager component in the Yii config to set routes for /article and /question to go to the same controller, and then either use different actions or different parameters to differentiate between the two. Since you said they are almost identical, I would suggest different parameters and a single action as follows:
array(
...
'components' => array(
...
'urlManager' => array(
'question/<\d+:id>' => 'mycontroller/myaction/type/question/id/<id>',
'article/<\d+:id>' => 'mycontroller/myaction/type/article/id/<id>',
),
),
);
Of course you'll have to modify that for your needs, but that's the general setup. Look here for more information: http://www.yiiframework.com/doc/guide/1.1/en/topics.url