Doctrine : how to manipulate a collection? - php

With symfony && doctrine 1.2 in an action, i try to display the top ranked website for a user.
I did :
public function executeShow(sfWebRequest $request)
{
$this->user = $this->getRoute()->getObject();
$this->websites = $this->user->Websites;
}
The only problem is that it returns a Doctrine collection with all the websites in it and not only the Top ranked ones.
I already setup a method (getTopRanked()) but if I do :
$this->user->Websites->getTopRanked()
It fails.
If anyone has an idea to alter the Doctrine collection to filter only the top ranked.
Thanks
PS: my method looks like (in websiteTable.class.php) :
public function getTopRanked()
{
$q = Doctrine_Query::create()
->from('Website')
->orderBy('nb_votes DESC')
->limit(5);
return $q->execute();
}

I'd rather pass Doctrine_Query between methods:
//action
public function executeShow(sfWebRequest $request)
{
$this->user = $this->getRoute()->getObject();
$this->websites = $this->getUser()->getWebsites(true);
}
//user
public function getWebsites($top_ranked = false)
{
$q = Doctrine_Query::create()
->from('Website w')
->where('w.user_id = ?', $this->getId());
if ($top_ranked)
{
$q = Doctrine::getTable('Website')->addTopRankedQuery($q);
}
return $q->execute();
}
//WebsiteTable
public function addTopRankedQuery(Doctrine_Query $q)
{
$alias = $q->getRootAlias();
$q->orderBy($alias'.nb_votes DESC')
->limit(5)
return $q
}

If getTopRanked() is a method in your user model, then you would access it with $this->user->getTopRanked()

In your case $this->user->Websites contains ALL user websites. As far as I know there's no way to filter existing doctrine collection (unless you will iterate through it and choose interesting elements).
I'd simply implement getTopRankedWebsites() method in the User class:
class User extends BaseUser
{
public function getTopRankedWebsites()
{
WebsiteTable::getTopRankedByUserId($this->getId());
}
}
And add appropriate query in the WebsiteTable:
class WebsiteTable extends Doctrine_Table
{
public function getTopRankedByUserId($userId)
{
return Doctrine_Query::create()
->from('Website w')
->where('w.user_id = ?', array($userId))
->orderBy('w.nb_votes DESC')
->limit(5)
->execute();
}
}

You can also use the getFirst() function
$this->user->Websites->getTopRanked()->getFirst()
http://www.doctrine-project.org/api/orm/1.2/doctrine/doctrine_collection.html#getFirst()

Related

Unable to create data through show method in Laravel 5.3

I am working on notifications I have 2 tables: one is notify and the other is notify_status. Through notify I am showing data like title and description and in notify_status I have field read_status which is by default 0. After I show it I want to change it to 1. I also have notify_id in it as a foreign key. This is my show method:
public function show($id)
{
$notify = Notify::find($id);
$notify_status = NotifyStatus::where('notify_id', $id)->get();
$user_data['read_status'] = 1;
$user = NotifyStatus::create($user_data);
return view('notify.desr')->with(compact('notify'));
}
But it isn't creating against notify_id. What should I do?
Your models should define the following relationships:
class Notify extends Model
{
public function setAsRead()
{
$this->status->read_status = 1
$this->status->save();
}
public function wasRead()
{
return (bool) $this->status->read_status;
}
public function status()
{
return $this->hasOne(NotifyStatus::class);
}
}
class NotifyStatus extends Model
{
public function notify()
{
return $this->belongsTo(Notify::class);
}
}
Take a look at Laravel Eloquent Relationships for further reading.
In your controller you can use it like:
$notify = Notify::find($id);
$notify->status->read_status = 1;
$notify->status->save();
return view('notify.desr')->with(compact('notify'));
Or you can simply create a new method to set the new status (take a look at first method of Notify class):
$notify = Notify::find($id);
$notify->setAsRead();
return view('notify.desr')->with(compact('notify'));
public function show($id)
{
$notify = Notify::find($id);
$notify_status = NotifyStatus::where('notify_id', $id)->first();
$notify_status->read_status = 1;
$notify_status->save()
return view('notify.desr')->with(compact('notify'));
}

Filtering Eloquent

I am trying to filter my Eloquent model collections based on if a user has access to the model or not.
My current method works, but it is really slow so I am wondering if there is a more performant way to do it?
I have a userHasAccess() method on every model in the collection.
It uses Laravel's ACL Features to determine if the user has access to the model:
public function userHasAccess()
{
if (Auth::user()->can('show', $this)) {
return true;
}
return false;
}
I then override the newCollection() method on the model:
public function newCollection(array $models = Array())
{
$collection = new Collection($models);
$collection = $collection->filter(function($model)
{
if($model->userHasAccess())
return true;
});
return $collection;
}
The policy method looks like this:
public function show(User $user, Quote $quote)
{
if(!$quote->customer)
return false;
if(($user->id === $quote->user_id))
return true;
if($user->hasRole(['super-admin','admin']))
return true;
return false;
}
Is there a better way to do this? Especially in terms of performance?
You could add the logic to the query and speed it up dramatically
$query = User::query();
if(!Auth::user()->hasRole(['super-admin','admin'])){
$query->where('user_id','=',Auth::id);
}
$data = $query->get();
You could do this on a wider scale using a scope
class User extends Model
{
public function scopeLimitByUser($query)
{
if(!Auth::user()->hasRole(['super-admin','admin'])){
$query->where('user_id','=',Auth::id);
}
}
}
Then for the quote customer you can add a where to the query
$query->whereNotNull('customer_id');

Codeigniter - Calling a model method within same model is buggy

I am trying to call a model method within same model and its not working as intended. Here is my class with two methods which do not work
class mymodel extends CI_Model{
public function __construct(){
parent::__construct();
$this->tablea = 'tablea';
$this->tableb = 'tableb';
}
public function saveData($data){
$dataCopy['revisionkey'] = $this->getRevisionKey($data['id']);
//check and condition revision key to be int with +1 from last one
$this->db->insert($this->tableb, $dataCopy);
$this->db->where('id', $id);
return $this->db->update($this->user_table, $data) ? true : false;
}
public function getRevisionKey($id){
$this->db->select($this->revision_tablea.'.revisions_number as revisions_number')
->from($this->revision_tablea)
->where($this->revision_tablea.'.id', $id)
->order_by($this->revision_table.'.revisions_number', 'asc')
->limit(1);
$query = $this->db->get();
if ($query->num_rows() > 0){
return $query->row_array();
}else{
return 0;
}
}
}
Now method getRevisionKey() should produce a query like following
SELECT `tableb`.`revisions_number` as revisions_number FROM (`tableb`) WHERE `tableb`.`id` = '26' ORDER BY `tableb`.`revisions_number` asc LIMIT 1
but it produces a query like following
SELECT `tableb`.`revisions_number` as revisions_number FROM (`tableb`) WHERE `id` = '26' AND `tableb`.`id` = '26' ORDER BY `tableb`.`revisions_number` asc LIMIT 1
This of course is due to same method being called within the model, this method works fine if used outside of model. Any solution to this problem?
EDIT
Rewriting the getRevisionKey() fixes this. Here is the new version
public function getRevisionKey($id){
$sqlQuery = $this->db->select($this->revision_tablea.'.revisions_number as revisions_number')
->from($this->revision_tablea)
->order_by($this->revision_table.'.revisions_number', 'asc')
->limit(1);
$query = $sqlQuery->where($this->revision_tablea.'.id', $id)->get();
if ($query->num_rows() > 0){
return $query->row_array();
}else{
return 0;
}
}
Here is a simple hack that will give you exactly where you are making a mistake.
Go to system/database/DB_active_rec.php Remove public or protected keyword from these functions
public function _compile_select($select_override = FALSE)
public function _reset_select()
And save it. Before running the function i mean calling
$this->db->get() // Use $this->db->from() instead
use
$query = $this->db->_compile_select()
and echo $query;
These two functions also help in subquery in codeigniter active record.
How can I rewrite this SQL into CodeIgniter's Active Records?

How to properly load a Database row into a class in php

I am using Kohana 3.2, not that it matters much, but I am writing an application where I am wrapping any and all returned database records in a specific class. I don't know that I understand the best way to load the database row into the class, because sometimes it is a ORM model and sometimes it might just be an row ID.
So from a controller if I wanted a list of all the users it would look something like:
Controller:
$users = User::find_all();
User Class
public static function find_all()
{
$users_model = ORM::factory('user')->find_all();
$users = array();
foreach ($users_model as $user_model)
{
$users[] = User::instance($user_model);
}
return $users;
}
That works great, but sometimes I need to load a user object with just an id, like after some action, again a example:
Controller
$user_id = $_POST['user_id'];
$user = User::instance($user_id);
So is the User class responsible for trying to identify if an ID or a ORM object was passed into it. It seems like that isn't right for good OOP practices, but I am really not sure what the best way to do it is. What I have been currently doing is in the construct:
public function __construct($user, $load = 'model')
{
if ($load == 'model')
{
$this->user_model = $user;
}
if ($load == 'id')
{
$this->user_model = ORM::factory('user', $user);
}
}
But that really just doesn't feel right. Any advice would be greatly appreciated.
If you already have a user model that extends ORM then you probably don't need static methods in the class to get all users or to get a particular user.
In your controller you can just do to get all the users
$users = ORM::factory('user')->find_all();
To get a single user
$user = ORM::factory('user', $user_id);
If you still want to go down the wrapping route you could use functions like so
public static function all() {
$users = ORM::factory('user')->find_all();
if (count($users)) {
return $users;
}
return false;
}
public static function get($user_id) {
$user = ORM::factory('user', $user_id);
if ($user->id) {
return $user;
}
return false;
}
In your controller, use the ORM to get the user based on the ID.
$user_id = $_POST['user_id'];
$user = ORM::factory('user', $user_id);
Or given that you already have a find all in your user class, add a find_one() method:
User Class
public static function find_one($user_id)
{
$user = ORM::factory('user', $user_id);
return $user;
}
Controller
$user_id = $_POST['user_id'];
$user = User::find_one($user_id);
See the Kohana docs.
http://kohanaframework.org/3.1/guide/orm/using#finding-an-object
http://kohanaframework.org/3.1/guide/orm/examples/simple
Maybe like this:
public function __construct($user)
{
$this->user_model = is_object($user) ? $user : ORM::factory('user', (int)$user);
}

filter some data(row) in zend view

I have
1.Table:user(userId,userName,userGroup)
2.Model:userModel
3.usercontroller
there i a simple code:
Controller:
class UserController extends Zend_Controller_Action
{
public function getuser()
{
$userModel = new userModel();
$this->view->usergroup = $userModel;
}
}
Model:
class Model_UserGroupModel extends Zend_Db_Table_Abstract {
public function getuser(
{
$select = $this->select();
return $this->fetchAll($select);
}
}
view:
please tell me what code I must insert in view to only have user with specific row like user with group teacher also i use partialoop???
foreach ($this->usergroup->toArray() as $userGroup) {
echo $userGroup['row'];
}
should work
To filter rows based on a particular field like userGroup you need to do a query with where condition.
$where = $this->getAdapter->quoteInto("userGroup = ?", 'teacher');
$select = $this->select()->where($where);
So now you can get users for a particular user group.

Categories