Model save mutiple records and validation rules - yii

I have a case where user can select multiple values in the list box and save it to the database using model.
Here is the table structure
user_id int(11) , cars_id int(5)
Here is the snippet of my view
<?php echo CHtml::dropDownList("sourceCars", '',CHtml::listData(MasterCars::model()->findAll(),'cars_code','car_name'),array('size'=>20) );?>
<?php echo CHtml::dropDownList("targetCars", '', array(),array('size'=>20) );?>
User selects the cars from sourceCars and moves into targetCars using Jquery ( This part is done) and
clicks on Save or Submit button .
Now I should be able to save all the cars he/she selected in the targetCars list. Moreover in model I should put a condition that user can't save more than 10 cars and at least one car should be selected . Also user can select 5 cars at one time and next time when he comes he should be able to select max 5 cars only since he already save 10 records .
Could you please throw me some idea to implement this ? any Links that can guide me ?

your question is to limit selection of cars between 1-10.
You need validate user input both client and server.
At server,you can custom a ActiveRecord validation
public function rules()
{
return array(
array('cards_id', 'limitSelect','min'=>1,'max'=>10),
);
}
public function limitSelect($attribute,$params)
{
//and here your code to get the count of selection of cars for a user
...
if($count<=$params['min'])
$this->addError('cards_id','at least one car should be selected');
if($count>=$params['max'])
$this->addError('cards_id',' can't select more than 10 cars');
}
//and for mutiple select you can code this:
echo CHtml::dropDownList("sourceCars", '',CHtml::listData(MasterCars::model()->findAll(),'cars_code','car_name'),array('size'=>20,'multiple'=>true) );
//anyway you can implement it in several way

Sounds like you want to use scenarios, see docs here. You can dynamically set the scenario with CModel::setScenario based on the user flow.

Related

Eloquent: Get pages based on user role

I have a User, Role & Page setup, all with many-to-many relationships and the pivot tables setup in the usual fashion (role_user, page_role), along with the eloquent methods to attach a model to the pivot tables.
My idea is to allow a user to have many roles, and a page can be accessed by many roles.
However now I'd like to return a collection where I have my users details and then the pages they're allowed to access.
The closest I have got is:
return User::find( Auth::user()->id )->with('roles.pages')->first()->roles;
Now this returns each role the user has, and each page that the role can access. Which is correct, however I have duplication on the pages part.
How would I go about getting only a list of pages the user is able to access with no duplication?
Cheers
Read that answer to get you on the track: HasManyThrough with one-to-many relationship
Only for your setup you need to adjust the query - join 2 pivot tables (and make sure they represent real data, ie no rows referencing non-existing models):
// User model
// accessor so you can access it like any relation: $user->pages;
public function getPagesAttribute()
{
if ( ! array_key_exists('pages', $this->relations)) $this->loadPages();
return $this->getRelation('pages');
}
// here you load the pages and set the collection as a relation
protected function loadPages()
{
$pages = Page::join('page_role as pr', 'pr.page_id', '=', 'pages.id')
->join('role_user as ru', 'ru.role_id', '=', 'pr.role_id')
->where('ru.user_id', $this->id)
->distinct()
->get(['pages.*', 'user_id']);
$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(Page::query(), $this, 'user_id', 'id');
$hasMany->matchMany(array($this), $pages, 'pages');
return $this;
}
One more thing: I hardcoded tables and columns names for sake of simplicity, but in real life I suggest you rely on the relationships and their getters, like: $relation->getTable(), $relation->getForeignKey() etc.
Now suggestion about your code:
return User::find( // 2. query to get the same user
Auth::user()->id // 1. query to get the user and his id
)->with('roles.pages')
->first() // 3. query to get ANOTHER user (or the same, luckily..)
->roles;
Use Auth::id() instead of Auth::user()->id (for Laravel ver 4.1.25+) to avoid redundant query
find() and first() are methods that execute the query, so you just returned the user with id = Auth::user()->id and moment later you fetch another one, who comes first() from the users table..
You don't need to use User::whatever for authenticated user, use Auth::user() instead.
So the code with suggested solution would look like this:
Auth::user()->pages; // collection of Page models with unique entries

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

Yii-CRUD: Getting select boxes filled with data from other tables

Do I need some more work, to get a select box with the corresponding data (e.g. land list from an another db-table) in the created insert form (via CRUD) or it is enough to define the relations in the models and yii would do this for me automatically?
Since you haven't provided any code, let me show you with an example. Suppose we have a user table and a group table and need to select a group for a user which is selected with a select box.
In the user model you can have a function like
public function getGroupName()
{
return CHtml::listData(Group::model()->findAll();
}
In the form view of user create you can populate the select box like below:
<?php echo $form->dropDownListRow($User, 'group_id', $User->getGroupName(),array('prompt' => 'Select ...')); ?>

Dropdown without model in yii

I am using a component for parsing a country api in yii. So in the form drop down list call the function for listing country. The function returned country list as array.
form.php
<?php echo $form->labelEx($model,'country'); ?>
<?php $cty= Country::getCountry();
echo $form->dropdownList($model,'country', $cty , array('style'=>'width: 175px','empty'=>array('empty'=>Yii::t('app','Select Country'))));?>
Now the country list loaded correctly in drop down, but when on saving time the corresponding id of country is saved. i want to save the country name in db.How it solved?
You have to build your own custom array with the needed keys/values, e.g. :
$cty = Country::getCountry();
$cty = array_combine(array_values($cty), $cty);
You can use this way (in case if you need all items of Country table)
$cty = CHtml::listData(Country::model()->findAll(), 'name', 'name');

Drupal: Display a list of contribution (posted content) for each user

I’m searching for a way to display on a member profile page, the number of contributions in some content types. Basically it has to display something like this:
Blog(10)
Articles(10)
Questions(19)
Comments(30)
Tips(3)
I’ve installed some different modules (like “user stats”) that I though could help me but haven’t been successful.
I’m wondering if it would be easiest just to hard-code it into my template file by starting taking the uid and just run some queries with the content types I want to display but I’m not sure on how to do that either.
Any help og suggestions would be very much appreciated.
Sincere
- Mestika
Edit:
I found a solution to do it manually with a query for each content type but I'm still very interested in a solution that's more elegant and smoother.
I use this code:
global $user;
$userid = $user->uid;
$blog_count = db_result(db_query("SELECT COUNT(0) AS num FROM {node} n where n.type = 'blog' AND status = 1 AND n.uid = {$userid}"));
If you are using the core Profile module, you could use something like below. It will show the nodes created by the user whose profile is being viewed. As an added benefit, it only needs to execute one custom database query.
Insert this snippet into template.php in your theme's folder and change "THEMENAME" to the name of your theme:
function THEMENAME_preprocess_user_profile(&$variables) {
// Information about user profile being viewed
$account = $variables['account'];
// Get info on all content types
$content_types = node_get_types('names');
// Get node counts for all content types for current user
$stats = array();
$node_counts = db_query('SELECT type, COUNT(type) AS num FROM {node} WHERE status = 1 AND uid = %d GROUP BY type', $account->uid);
while ($row = db_fetch_array($node_counts)) {
$stats[] = array(
'name' => $content_types[$row['type']],
'type' => $row['type'],
'num' => $row['num'],
);
}
$variables['node_stats'] = $stats;
}
Now, in user-profile.tpl.php can add something similar to:
// If user has created content, display stats
<?php if (count($node_stats) > 0): ?>
// For each content type, display a DIV with name and number of nodes
<?php foreach ($node_stats as $value): ?>
<div><?php print $value['name']; ?> (<?php print $value['num']; ?>)</div>
<?php endforeach; ?>
// Message to show for user that hasn't created any content
<?php else: ?>
<?php print $account->name; ?> has not created any content.
<?php endif; ?>
This is just a general idea of what you can do. You can also add restrictions to the content types you look for/display, check permissions for users to see these stats, use CSS to change the look of the stats, etc.
If you are using Content Profile, you could use THEMENAME_preprocess_node() and check that the node is a profile node before executing this code.
Given your simple requirement and the fact that you have the SQL statement in-hand, I'd say just use that. There's no reason to add yet another module to your site and impact it's performance for the sake of a single query.
That said, from a "separation of concerns" standpoint, you shouldn't just drop this SQL in your template. Instead, you should add its result to the list of available variables using a preprocess function in your template.php file, limiting its scope to where you need it so you're not running this database query on any pages but the appropriate profile page.