Yii: Use a different scope based on user role - php

I have two levels of admins who can create and update users, "Admin" and "Manager".
In my User _form.php I have a "user_type" dropdown that has my account levels.
I want to limit managers from creating "Admin" accounts, and obviously hide the dropdown entirely when a user is updating their own record.
a) Is there a way to use rules() to control this behaviour?
b) I thought about creating a scope called "hideAdmin" which would look like this:
'hideAdmin' => array(
'condition' => 'user_type != "Admin"',
),
and then creating a public method in my model called "scopeToUse()" which looks like this:
public function scopeToUse() {
$scope = 'hideAdmin()';
if(Yii::app()->authManager->checkAccess('Admin', Yii::app()->user->id)) {
$scope = 'orderAsc()';
}
return $scope;
}
And finally, creating my dropdown list like this.
<?php echo $form->dropDownList($model,'user_type_id',
CHtml::listData(UserType::model()->scopeToUse()->findAll(),'id','user_type')); ?>
I was hoping 'scopeToUse()' would return the scope name and it would work, but I just end up getting this back:
Fatal error: Call to a member function findAll() on a non-object
Any idea on the right way to do this?
EDIT
I ended up taking a cue from #Rafay-Zia-Mir and creating a new method in my user class that checked permissions and returned the appropriate CHtml::listData along with the scope I wanted. Wasn't exactly what I had intended, but the end result is the same, it kept me from putting too much code in my view, and it allowed me to use my scopes instead of duplicating the selection criteria.
This was the code:
public function userTypeDropdown() {
if(Yii::app()->authManager->checkAccess('Admin',Yii::app()->user->id)) {
$listData = CHtml::listData(UserType::model()->findAll(),'id','user_type');
} else {
$listData = CHtml::listData(UserType::model()->hideAdmin()->findAll(),'id','user_type');
};
return $listData;
}

Ok actually you can do this by using If statement in your View code. You can do like this
<?php
if(Yii::app()->authManager->checkAccess('Admin', Yii::app()->user->id)) {
?>
<?php $criteria=new CDbCriteria();
$criteria->condition="NOT user_type=Admin";
echo $form->dropDownList($model,'user_type_id',
CHtml::listData(UserType::model()->findAll($criteria),'id','user_type')); ?>
<?php } ?>
If the user is admin only then the dropdown will be shown.
EDIT: If you want to get it using function call then you can use this.
public function scopeToUse() {
if(Yii::app()->authManager->checkAccess('Admin', Yii::app()->user->id)) {
$this->getDbCriteria()->mergeWith(array(
'condition' => 'NOT user_type="Admin"',
'order'=>'id ASC'
));
}
return $this;
}
then you can use use
<?php echo $form->dropDownList($model,'user_type_id',
CHtml::listData(UserType::model()->scopeToUse()->findAll(),'id','user_type')); ?>

Related

Calling function with return value in cakephp

I am new to cakephp. I have a problem with calling the function. here is my issue.
In Contrloller file i get all the values using the following function
public function index()
{
$conditions = array(
'order' => array('Histroy.chat_sk DESC')
);
$this->set('histroys', $this->Histroy->find('all',$conditions));
}
In My model file have the following,
class Histroy extends AppModel
{
public $tablePrefix = 'plc_';
public $useTable = 'chat_history';
}
In my view file i have listed the values using foreach() function and that as follows
foreach ($histroys as $histroy):
$oper_name = $histroy['Histroy']['operator_fk'];
$operator_email = $histroy['Histroy']['email'];
endforeach
in that opertaor_fk is a field in history table. So i need get the operator name by another table as operators. So i need to call that function in the view.
Ex : In core we can do like as,
$operator_name = operator_name($fetch['operator_id']);
Function should be like this:
function operator_name($id)
{
// Select the value for the matched field in the operator
return $operator_name;
}
In cakephp how can i retrieve the values.
Please help me out to fix this. Thanks in Advance
Follow the blog tutorial for cake. It'll explain how to create associations and relationships between tables to let you do what is is you want, but in a nutshell, you need to create a relationship between History and Operator models and work from there.

Kohana/PHP- adding more information to a record before passing it to the view

Scenario: I have a view to which i pass a kohana record-set object, and view will go through each record and display its values in appropriate format. In the view i have to alert user with a message. This alert will only get activated if certain criterion is met, for example, view have to do PHP datediff using the date property of each record and see whether it is the best time to display the alert to user.
What i am thinking is instead of the view do the calculation, i would do it inside the controller. However, I believe it is a bad idea inside the controller to have a loop which iterates through each record and do the calculation and wraps the record inside a wrapper object along with additional properties specific for view. What i am looking for something like a callback in the model which gets called every time, a record is fetched. I can do the my calculations there and return the object. Is such an approach possible with Kohana? If not, please tell me what is the best solution to fit in this requirement?
Thanks for your time and attention.
Yes indeed, your suggestion is a good one and I use it all the time.
I assume you have a model which extends ORM. As an example, you could do something like this.
class Model_Example extends ORM {
protected $_primary_key = 'id';
protected $_table_name = 'example_table';
// possibly relations, filters etc.
function should_message_be_shown() {
if( !$this->_loaded ) {
throw new Kohana_Exception('Should only be called on loaded objects');
}
if( $this->date_created > ( time() - 3600 ) ) {
return true;
}
return false;
}
function get_user_message() {
if( !$this->_loaded ) {
throw new Kohana_Exception('Should only be called on loaded objects');
}
return 'Hi ' . $this->user_name . '! This is your personal message';
}
}
The method should_message_be_shown() makes use of the class variable Model_Example::date_created. This assumes you have a table column called date_created which holds an UNIX timestamp. So in this particular example it returns true if the record was created within the last hour.
I have added a check to see if the record is actually loaded and throw an exception otherwise.
To complete the example, you can fetch and use the model in your controller and view like this:
class Controller_Example extends Controller {
function action_index() {
$records = ORM::factory('Example')->where('something', '=', true)->find_all();
$this->response->body(View::factory('example')
->set('examples', $records));
}
}
And the view file:
<div class="example-list">
<?php foreach($examples as $example): ?>
<div class="example-item">
<h2><?php echo $example->title; ?></h2>
<?php if($example->should_message_be_shown()): ?>
<div class="message">
<?php echo $example->get_user_message(); ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>

how to echo out data using findAllBySql in active record

This should return a list of about five locations. It returns nothing with no errors. I've tested the sql using mysql workbench. It returns the data just fine. Right now I'm writing the back end so I'm not concerned with using views or the dataprovider. I'm just making sure my back end functions work. So with that in mind, how would you return the data retrieved by findAllBySql?
class CashLogic
{
public function AllLocations()
{
$model = new Locations;
$allLocations = $model->findAllBySql("SELECT name from locations");
return $allLocations;
}
}
class SiteController extends Controller
{
public function actionIndex()
{
$model = new CashLogic;
$data = $model->AllLocations();
return $data;
}
}
The findAllBySql() method returns an array of models. From your code it seems you only want the names of locations. An alternative method is
$AllLocations=CHtml::listData(Locations::model()->findAll(),'name','name');
This will return an array of the form array('name'=>'name','name'=>'name'). A better solution would be to replace the first name with the primary key of your locations table.

Pass findBy parameter to callback method afterFind

I am trying to catch a certain findBy call (with afterFind) where:
if $results is empty (or the value you are trying to find is nonexistent), but the parameter value is found on another table, then it will modify $results to be valid
Some controller action got this:
$this->User->findByUsername("Bingo"); // yes, username Bingo doesnt exist on users table
User model:
function afterFind($results, $primary){
if(empty($results)) {
if(in_array($findbyparameter, array("Bingo", "Bingo1", "Bingo2"))) {
// modify $results
}
}
}
The problem is, how do I get $findbyparameter?
Thanks! All help will be appreciated!
I am not using these convenience methods, but you can pass the variable as Model property like this:
//where you search
$this->User->searchPhrase = "Bingo";
findByUsername($this->User->searchPhrase);
//Model
function afterFind($results, $primary){
if(empty($results)) {
if(in_array($this->searchPhrase, array("Bingo", "Bingo1", "Bingo2"))) {
// modify $results
}
}
}
It's not the prettiest method, but I guess it would work. Try to print_r($this) in afterFind method and see if you can spot somewhere the phrase which you search. I believe it's passed in the condition's array.
Perhaps a custom find type is what you're looking for. Custom find types have two states: before and after.
In the before you would setup your condition, and in the after you would check your data and modify if necessary. In both states you will have access to the query options.
Setting up custom finds is slightly different in 1.x and 2.x (you haven't mentioned which version you're using), so you can look up the specifics in the book.
In short, you would add add your the find type into the $findMethods property of the model and then add the corresponding method name to your model. Say you call your custom find type 'byUsername'
protected function _findByUsername($state, $query, $results = array()) {
if ($state === 'before') {
// add your condition to the query,
return $query;
} elseif ($state === 'after') {
// modify $results if you need to
return $results;
}
}
And you would call it via $this->User->find('byUsername', array('username' => $username));
In $query you would have the key 'username' which you can add to the conditions key of $query. In both states, you would have access to $query['username'].

CodeIgniter get_where

I’m attempting to use get_where to grab a list of all database records where the owner is equal to the logged in user.
This is my function in my controller;
function files()
{
$owner = $this->auth->get_user();
$this->db->get_where('files', array('owner =' => '$owner'))->result();
}
And in my view I have the following;
<?php foreach($query->result() as $row): ?>
<span><?=$row->name?></span>
<?php endforeach; ?>
When I try accessing the view, I get the error :
Fatal error: Call to a member function result() on a non-object in /views/account/files.php on line 1.
Wondered if anyone had any ideas of what might be up with this?
Thanks
CodeIgniter is a framework based on MVC principles. As a result, you would usually separate application logic, data abstraction and "output" into their respective areas for CodeIgniter use. In this case: controllers, models and views.
Just for reference, you should usually have you "data" code as a model function, in this case the get_where functionality. I highly suggest you read through the provided User Guide to get to grips with CodeIgniter, it should hold your hand through most steps. See: Table of Contents (top right).
TL;DR
To solve your problem you need to make sure that you pass controller variables through to your view:
function files()
{
$owner = $this->auth->get_user();
$data['files'] = $this->db->get_where('files', array('owner =' => '$owner'))->result();
$this->load->view('name_of_my_view', $data);
}
And then make sure to use the correct variable in your view:
<?php foreach($files as $row): ?>
<span><?=$row['name']; ?></span>
<?php endforeach; ?>
<?php foreach($query->result() as $row): ?>
<span><?=$row->name?></span>
<?php endforeach; ?>
Remove the result function like so.
<?php foreach($query as $row): ?>
<span><?=$row->name?></span>
<?php endforeach; ?>
Btw. It's a much better idea to test the query for a result before you return it.
function files()
{
$owner = $this->auth->get_user();
$query = $this->db->get_where('files', array('owner =' => $owner))->result();
if ($query->num_rows() > 0)
{
return $query->result();
}
return FALSE;
}
public function get_records(){
return $this->db->get_where('table_name', array('column_name' => value))->result();
}
This is how you can return data from database using get_where() method.
All querying should be performed in the Model.
Processing logic in the View should be kept to an absolute minimum. If you need to use some basic looping or conditionals, okay, but nearly all data preparation should be done before the View.
By single quoting your $owner variable, you convert it to a literal string -- in other words, it is rendered as a dollar sign followed by five letters which is certainly not what you want.
The default comparison of codeigniter's where methods is =, so you don't need to declare the equals sign.
I don't know which Auth library you are using, so I'll go out on a limb and assume that get_user() returns an object -- of which you wish to access the id of the current user. This will require ->id chained to the end of the method call to access the id property.
Now, let's re-script your MVC architecture.
The story starts in the controller. You aren't passing any data in, so its duties are:
Load the model (if it isn't already loaded)
Call the model method and pass the owner id as a parameter.
Load the view and pass the model's returned result set as a parameter.
*Notice that there is no querying and no displaying of content.
Controller: (no single-use variables)
public function files() {
$this->load->model('Files_model');
$this->load->view(
'user_files',
['files' => $this->Files_model->Files($this->auth->get_user()->id)]
);
}
Alternatively, you can write your controller with single-use variables if you prefer the declarative benefits / readability.
public function files() {
$this->load->model('Files_model');
$userId = $this->auth->get_user()->id;
$data['files'] = $this->Files_model->Files($userId);
$this->load->view('user_files', $data);
}
Model: (parameters are passed-in, result sets are returned)
public function Files($userId) {
return $this->db->get_where('files', ['owner' => $userId])->result();
}
In the above snippet, the generated query will be:
SELECT * FROM files WHERE owner = $userId
The result set (assuming the query suits the db table schema) will be an empty array if no qualifying results or an indexed array of objects. Either way, the return value will be an array.
In the final step, the view will receive the populated result set as $files (the variable is named by the associative first-level key that was declared in the view loading method).
View:
<?php
foreach ($files as $file) {
echo "<span>{$file->name}</span>";
}
The { and } are not essential, I just prefer it for readability in my IDE.
To sum it all up, the data flows like this:
Controller -> Model -> Controller -> View
Only the model does database interactions.
Only the view prints to screen.

Categories