How to use database functions in Kohana 3 ORM select query - postgresql

I'm using Postgres with Kohana 3's ORM module and would like to run a SELECT using a postgres function to convert values already in the database to lower case before doing the comparison.
In SQL I would write:
select * from accounts where lower(email) = 'alice#spam.com';
In Kohana I would like to write something like this:
$user = ORM::factory('user')
->where('lower(email)', '=', strtolower('alice#spam.com'))
->find();
But this gives an error because ORM is trying to deduce the column name as 'lower(email)' rather than just 'email'.
I'm new to Kohana and ORM so alternatives that would give me the same result would be useful too.

Or IMHO even beter, try this:
$user = ORM::factory('user')
->where('LOWER("email")', '=', DB::expr("LOWER('alice#spam.com')"))
->find();
PS. I do not see any need to create a DB::lower() helper, but that might just be me...
EDIT:
$value = 'alice#spam.com';
$user = ORM::factory('user')
->where('LOWER("email")', '= LOWER', (array) $value)
->find();
The query will become something like (havent used ORM in a while) "SELECT users.id, users.email FROM users WHERE LOWER("email") = LOWER ('alice#spam.com') LIMIT 1".
Notice the space, I just updated some of my code to use this since I just figured out this posibility.
I hope you will be as happy with it as I am.

try this:
$user = ORM::factory('user')
->where(DB::expr('lower(email)'), '=', strtolower('alice#spam.com'))
->find();

I'm not completely happy with the use of a helper but I use it a couple other classes so it's nice to keep the logic in one location. Here is what I'm currently using.
class DB extends Kohana_DB
{
public static function lower($value)
{
return DB::expr('lower('.Database::instance()->quote($value).')');
}
}
class Model_User extends Model_Base
{
public static function find_by_email($email)
{
$user = ORM::factory('user')
->where(DB::expr('lower(email)'), '=', DB::lower($email))
->find();
return $user;
}

Related

Doctrine - postgresql, search in a json array

We are working with Symfony and Doctrine ORM. We mostly use MySQL / MariaDB, and for a new project we are considering trying PostgreSQL.
Obviously using an ORM like Doctrine is doing a lot of the work for us, and 99% of our requests are fine.
I'm stuck with a specific request, when I'd like to filter our users by their role. We normally do something like this, using LIKE:
public function findByRole(string $role): array
{
return $this->createQueryBuilder('u')
->andWhere('u.roles LIKE :role')
->setParameter('role', '%"'.$role.'"%')
->orderBy('u.id', 'ASC')
->getQuery()
->getResult()
;
}
This is unfortunately not working with postgresql, and after searching on this forum I found a solution using JSON_GET_TEXT. But my problem is that we have to give the index of the array to search in, and I don't know where my role could be.
So I have this somewhat working function right now, but it is so ugly :
/**
* Search all the users having a specific role.
*
* #param string $role - The role to search for in user's roles
*
* #return mixed
*/
public function findByRole(string $role): mixed
{
// TODO: Il y a probablement mieux à faire...
return $this->createQueryBuilder('u')
->andWhere('JSON_GET_TEXT(u.roles,0) = :role')
->orWhere('JSON_GET_TEXT(u.roles,1) = :role')
->orWhere('JSON_GET_TEXT(u.roles,2) = :role')
->orWhere('JSON_GET_TEXT(u.roles,3) = :role')
->orWhere('JSON_GET_TEXT(u.roles,4) = :role')
->orWhere('JSON_GET_TEXT(u.roles,5) = :role')
->orWhere('JSON_GET_TEXT(u.roles,6) = :role')
->orWhere('JSON_GET_TEXT(u.roles,7) = :role')
->orWhere('JSON_GET_TEXT(u.roles,8) = :role')
->orWhere('JSON_GET_TEXT(u.roles,9) = :role')
->setParameter('role', $role)
->getQuery()
->getResult();
}
How can I improve my function to work on postgresql? And more importantly, is there a solution that could work on both MySQL and PostgreSQL with Doctrine so we could decide to switch seamlessly between the two database engines?
I don't know if my answer is related with your problem.
In PostgreSQL "user" is a reserved keyword, so you can't create database named USER in postgre. But Symfony found a solution and switchs the name directly to 'user' with parentheses.
What I would like to say is that you may face problems while using 'User' name in postgre so it's recomended to name your table 'account' instead of 'user' here.

Laravel 5.1 - Eloquent Relationships

I am new to Laravel. I was looking at the Eloquent relationships. I followed the documentation and defined my one to many relationship as:
// in the test model
public function questions()
{
return $this->hasMany('App\Question', 'test_name_id', 'test_id');
}
// in the question model
public function test()
{
return $this->belongsTo('App\Test', 'test_name_id', 'test_id');
}
Note that i have not followed the convention for naming the id's. So if I do this in the Tinker:
$test = App\Test::first();
$question = $test->questions;
It works fine. But if want the record any other than first like doing:
$test = App\Test::where(['test_id'=>'2'])->get();
$question = $test->questions;
It gives me this error:
PHP error: Trying to get property of non-object on line 1
Can any one please explain me what I am doing wrong here and how does this work properly?
Try ->first() instead of get(). get() returns array. Or use protected $primaryKey = 'test_id'; in test model and then ->find($id)

Using `DATE()` in Doctrine Querybuilder

I need to get all rows where DATE(a.when) matches the string 2014-09-30.
$builder = $this->em->createQueryBuilder();
$builder->select('a')
->from('Entity\Appointment', 'a')
->andWhere('a.when = :date')
->setParameter('date', $date);
a.when is a full DATETIME; :date is only a string (in DATE format).
The following and variations didn't work:
->andWhere('DATE(a.when) = :date')
Error: Expected known function, got 'DATE'
What's the correct usage here?
Thanks to andy, using this now:
$builder = $this->em->createQueryBuilder();
$builder->select('a')
->from('Entity\Appointment', 'a')
->andWhere('a.when >= :date_start')
->andWhere('a.when <= :date_end')
->setParameter('date_start', $date->format('Y-m-d 00:00:00'))
->setParameter('date_end', $date->format('Y-m-d 23:59:59'));
This actually is a very common question.
It turns out that not all sql databases support a DATE function, so the good people in charge of Doctrine decided not to support it nativelly.
Kind of wish they did because it would have saved a bunch of people a fair amount of effort.
So add this rather magical class to your project:
namespace Cerad\Bundle\CoreBundle\Doctrine\DQL;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
class Date extends FunctionNode
{
public $date;
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
{
return "DATE(" . $sqlWalker->walkArithmeticPrimary($this->date) . ")";
}
public function parse(\Doctrine\ORM\Query\Parser $parser)
{
$parser->match(Lexer::T_IDENTIFIER);
$parser->match(Lexer::T_OPEN_PARENTHESIS);
$this->date = $parser->ArithmeticPrimary();
$parser->match(Lexer::T_CLOSE_PARENTHESIS);
}
}
Then wire it up in the doctrine section of your app/config.yml:
doctrine:
orm:
default_entity_manager: default
auto_generate_proxy_classes: %kernel.debug%
entity_managers:
default:
connection: default
...
dql:
datetime_functions:
date: Cerad\Bundle\CoreBundle\Doctrine\DQL\Date
http://doctrine-orm.readthedocs.org/en/latest/cookbook/dql-user-defined-functions.html
http://symfony.com/doc/current/cookbook/doctrine/custom_dql_functions.html
http://symfony.com/doc/current/reference/configuration/doctrine.html
There are other bundles out there with more sql functions. Oddly enough, the first time I looked a few years ago, none of them had Date defined. So I just made my own.
====================================================================
Update 01
I did not check the tags carefully and assumed that this was a Symfony 2 application. The Date class stays the same. You wire it up by getting the doctrine configuration object.
$config = new \Doctrine\ORM\Configuration();
$config->addCustomDatetimeFunction('DATE', 'blahblah\Date');
Check the Doctrine link for details.
A different approach using $qb->expr()->between() in the same andWhere:
$builder = $this->em->createQueryBuilder(); $builder->select('a')
->from('Entity\Appointment', 'a')
->andWhere($qb->expr()->between('a.when', ':date_start', ':date_end'))
->setParameter('date_start', $date->format('Y-m-d 00:00:00'))
->setParameter('date_end', $date->format('Y-m-d 23:59:59'));

Laravel 4 Auth with Facebook (no password authentication)

I'm trying to set up an authentication system with Laravel 4 with a Facebook login. I am using the madewithlove/laravel-oauth2 package for Laravel 4.
Of course, there is no password to add to my database upon a user loggin in with Facebook. I am, however, trying to check to see if a user id is in the database already to determine if I should create a new entity, or just log in the current one. I would like to use the Auth commands to do this. I have a table called "fans".
This is what I'm working with:
$fan = Fan::where('fbid', '=', $user['uid']);
if(is_null($fan)) {
$fan = new Fan;
$fan->fbid = $user['uid'];
$fan->email = $user['email'];
$fan->first_name = $user['first_name'];
$fan->last_name = $user['last_name'];
$fan->gender = $user['gender'];
$fan->birthday = $user['birthday'];
$fan->age = $age;
$fan->city = $city;
$fan->state = $state;
$fan->image = $user['image'];
$fan->save();
return Redirect::to('fans/home');
}
else {
Auth::login($fan);
return Redirect::to('fans/home');
}
Fan Model:
<?php
class Fan extends Eloquent {
protected $guarded = array();
public static $rules = array();
}
When I run this, I get the error:
Argument 1 passed to Illuminate\Auth\Guard::login() must be an instance of Illuminate\Auth\UserInterface, instance of Illuminate\Database\Eloquent\Builder given
EDIT: When I use: $fan = Fan::where('fbid', '=', $user['uid'])->first();
I get the error:
Argument 1 passed to Illuminate\Auth\Guard::login() must be an instance of Illuminate\Auth\UserInterface, null given, called in /Applications/MAMP/htdocs/crowdsets/laravel-master/vendor/laravel/framework/src/Illuminate/Auth/Guard.php on line 368 and defined
I do not know why it is giving me this error. Do you have suggestions on how I can make this work? Thank you for your help.
You have to implement UserInterface to your model for Auth to work properly
use Illuminate\Auth\UserInterface;
class Fan extends Eloquent implements UserInterface{
...
public function getAuthIdentifier()
{
return $this->getKey();
}
/**
* Get the password for the user.
*
* #return string
*/
public function getAuthPassword()
{
return $this->password;
}
}
getAuthIdentifier and getAuthPassword are abstract method and must be implemented in you class implementing UserInterface
To login any user into the system, you need to use the User model, and I bet inherited classes will do the trick as well but I'm not sure.
Anyway, your Fan model does not associate with the User model/table in any way and that's a problem. If your model had a belong_to or has_one relationship and a user_id field then you could replace Auth::login($user) with Auth::loginUsingId(<some id>).
Original answer:
You are missing an extra method call: ->get() or ->first() to actually retrieve the results:
$fan = Fan::where('fbid', '=', $user['uid'])->first();
Alternatively, you can throw an exception to see what's going on:
$fan = Fan::where('fbid', '=', $user['uid'])->firstOrFail();
If you see different errors, update your question with those errors.

zend relationships with select

I am new to zend. I have been asked to redevelop a website that was once written in plain PHP and put it into the zend framework.
I am having a lot of trouble with database relationships, I cant seem to get my head round defining and querying relationships.
I would like to find a Category. From that Category I would like to be able to find all the CategoryInfo associated with it, and be able to query/sort/limit that dataset.
Here are my models.
Categorys.php
<?php
class Default_Model_Categorys extends Zend_Db_Table_Abstract
{
protected $_name = 'Categorys';
protected $_primary = 'id';
protected $_dependentTables = array('Default_Model_CategoryInfo');
}
?>
CategoryInfo.php
<?php
class Default_Model_CategoryInfo extends Zend_Db_Table_Abstract
{
protected $_name = 'Category_Info';
protected $_primary = 'id';
protected $_referenceMap = array(
'Categorys' => array(
'columns' => array('cat_id'),
'refTableClass' => 'Default_Model_Categorys',
'refColumns' => array('id')
)
);
}
?>
CategoryController.php
<?php
class CategorysController extends Zend_Controller_Action
{
public function indexAction()
{
/*
this should redirect to all games
*/
return $this->_forward("index", "games");
}
public function categoryAction()
{
/*
shows a specific category
*/
$id = (int) $this->_request->getParam('id');
$category = new Default_Model_Categorys();
$this->view->category = $category->fetchRow(
$category->select()->where('id = ?', $id)
);
$categoryInfo = $this->view->category->findDependentRowset('Default_Model_CategoryInfo');
}
}
Firstly... am I doing anything wrong?
Secondly... how do I go about querying the dependent rowset?
First, if you're searching for a category by its primary key, it's simpler to use the find() method:
$id = (int) $this->_request->getParam('id');
$category = new Default_Model_Categorys();
$this->view->category = $category->find($id)->current();
Second, to restrict or sort dependent Category_Info rows, you can use a Zend_Db_Table_Select object as an optional parameter of findDependentRowset(). Here's an example:
$select = $category->select()->where("info_type = 'PRICE'")
->order("info_date")
->limit(3);
$categoryInfo = $this->view->category->findDependentRowset(
'Default_Model_CategoryInfo', null, $select);
Notice you can use any table object to create that select object. Since the "FROM" clause for that select will be set by the findDependentRowset() method, you just add other clauses and then pass it in.
PS: You don't need to declare $_dependentTables at all, unless you're going to use cascading update or cascading delete via PHP code. I recommend strongly against doing that -- it's far more efficient to let the RDBMS handle those cascading operations.
Likewise you should never have to declare $_primary if your database tables actually declare primary key constraints. Zend_Db_Table_Abstract knows how to inspect metadata to get the primary key column(s).
Everything looks correctly to me. You don't query a dependent rowset. It is a query itself and it returns a result set. Basically what it is doing is pulling all records related to the current row you are working with as defined by $_referenceMap. Once you execute findDependentRowset(), you can foreach over the results which will give you instances of Zend_Db_Table_Row. From there you can display the related data as needed.
Personally I don't use Zend_Db Relationships. It is much easier to just make a second model method to query what I need. Also, Zend_Db Relationships do not support where clauses, so just making a second query is much more flexible than relationships.