I'm new in Cakephp 2.0, but I want to make a view of two tables with Inner Join.
I have the following tables tables:
hpsas with records: id, ciname, location, status
ldaps with records: id, ciname, status
The query I would do in MySQL is:
SELECT * FROM hpsas INNER JOIN ldaps ON hpsas.ciname = ldaps.ciname;
Which syntax do I have to use in whether the models, the controller or the view.
follow the activeRecord model i think you should define a relationship in the model to avoid running complex queries everytime you need records from this models. you will need to stick to cake's conventions tho and that means you will have 3 models(most possibly)
class cinema extends AppModel{
hasMany = array(hpsas,idaps);
}
class hpsas extends AppModel{
belongsTo = array(cinema)
}
class idap extends AppModel{
belongsTo = array(cinema)
}
with recursive set at above 2 or three one simple query to any one of the models will give you all the data you want(cake magic).
$options['joins'] = array(
array('table' => 'ldaps',
'type' => 'inner',
'conditions' => array(
'hpsas.ciname = ldaps.ciname',
)
)
);
$this->Hpsa->find('all', $options);
See Cake book section on associations for more details: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
Related
I have a joined table from siswa and kelas. in kelas table there is a column idSiswa, it comes from id from siswa table. the question is how I can get the id from kelas when its joined. when I try to fetch id it shows the id from siswa table, not from kelas table, I also already used right join and left join and still not get the answer
this is my kelas table
this is my siswa table
I using a query builder from laravel to run the query, and this is my query
$siswa = DB::table('siswas')
->join('kelas', 'kelas.idSiswa', '=', 'siswas.id')
->where([
['kelas.kelas', '2'],
['kelas.semester', 'ganjil'],
])
->select('kelas.*', 'siswas.*')
->get();
Your issue comes from a name conflict. When you join your two tables, there are 2 fields. To solve it, you should use SQL alias.
You can see an example on this topic
You could also consider using Eloquent which offers OOP advantages and automatically avoids this kind of issues.
namespace App;
use Illuminate\Database\Eloquent\Model;
class Kelas extends Model
{
public function siswa()
{
return $this->belongsTo('App\Siswa', 'idSiswa', 'kelas');
}
}
namespace App;
use Illuminate\Database\Eloquent\Model;
class Siswa extends Model
{
public function kelas()
{
return $this->hasMany('App\Kelas', 'idSiswa', 'kelas');
}
}
$siswa = App\Siswa::with('kelas')
->where([
['kelas', '2'],
['semester', 'ganjil'],
])
->get();
$firstSiswaKelasIds = $siswa->first()->kelas->map->id;
CakePHP 3.5.13 - working with a legacy database. I have a table called substances where the primary key is id. There is also another column in this table called app_id.
The majority of tables in the application have a foreign key which means they can be joined to substances.id. There is one table called article_95 which has a field article_95.app_id and therefore the join must be made to substances.app_id (not substances.id - there is no reference to that field inside the article_95 table at all).
The application performs a search based on up to 11 inputs. I'm using the ORM to dynamically build the query object before executing it.
I begin my query like this:
$query = $Substances->find()->select(['id' => 'Substances.id'])->distinct();
Then if I wanted to do something where the join maps to substances.id, I'm doing it like this:
// Search by CAS Number
if ($this->request->getData('cas_number')) {
$cas_number = $this->request->getData('cas_number');
$query = $query->matching('Cas', function ($q) use ($cas_number) {
return $q->where([
'Cas.value LIKE' => '%'.$cas_number.'%'
]);
});
}
So far so good. If I output the SQL string it looks like this:
'SELECT DISTINCT Substances.id AS `id` FROM substances Substances INNER JOIN cas_substances CasSubstances ON Substances.id = (CasSubstances.substance_id) INNER JOIN cas Cas ON (Cas.value like :c0 AND Cas.id = (CasSubstances.cas_id))'
My problem comes with how to manipulate the query object when it comes to my article_95 table, because it's trying to join on substances.id when I need it to join on substances.app_id.
I have the following in my Table classes. Please note the line $this->setPrimaryKey('tbl_id'); - this is because I'm using a legacy/old database and the primary key of the article_95 table is actually tbl_id not id. However this is relatively insignificant because the join should be based on app_id which exists in both tables.
// src/Model/Table/SubstancesTable.php
public function initialize(array $config)
{
$this->setTable('substances');
$this->setPrimaryKey('id');
$this->hasMany('Article95s', [
'foreignKey' => 'app_id'
]);
// ...
}
// src/Model/Table/Article95sTable.php
public function initialize(array $config)
{
$this->setTable('article_95');
$this->setPrimaryKey('tbl_id');
$this->belongsTo('Substances', [
'foreignKey' => 'app_id',
'joinType' => 'INNER'
]);
}
If I try and do a search which include an Article 95 value the SQL string becomes like this:
'SELECT DISTINCT Substances.id AS `id` FROM substances Substances INNER JOIN cas_substances CasSubstances ON Substances.id = (CasSubstances.substance_id) INNER JOIN cas Cas ON (Cas.value like :c0 AND Cas.id = (CasSubstances.cas_id)) INNER JOIN article_95 Article95s ON (Article95s.entity_name like :c1 AND Substances.id = (Article95s.app_id))'
The problem with this is the part of the SQL string which reads Substances.id = (Article95s.app_id)). I need that to be Substances.app_id = (Article95s.app_id)) but I don't know how to write this with the ORM syntax.
It's also important that the rest of the joins (e.g. CAS Number shown previously) remain joined on substances.id.
Please can someone help?
the manual explain it all
belongsTo
bindingKey: The name of the column in the other table, that will be used for matching the foreignKey. If not specified, the primary key (for example the id column of the Users table) will be used.
$this->belongsTo('Substances', [
'foreignKey' => 'app_id',
'bindingKey' => 'app_id',
'joinType' => 'INNER'
]);
hasMany
bindingKey: The name of the column in the current table, that will be used for matching the foreignKey. If not specified, the primary key (for example the id column of the Articles table) will be used.
$this->hasMany('Article95s', [
'foreignKey' => 'app_id',
'bindingKey' => 'app_id',
]);
I have two tables:
Table 1: tbluser
ID Name
1 .......... name1
2 .......... name2
3 .......... name3
Another table that links tbluser and tblrole
Table2: tbllinkuserrole
userid..........roleid
1 ............2
1 .......... 2
1 .......... 2
2 .......... 2
3 .......... 1
I would like to fetch the tbluser.Name and the user role from tbllinkuserrole in to a table: a single table row(user record) can have multiple rows inside the column 'role' based on the roles fetched from tbllinkuser role. A user can have multiple roles.
can you help me out...
Laravel have Models (Eloquent), in the model class are some functions that are easy to user to fetch data from table. (this works with Laravel 5.2)
The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table. https://laravel.com/docs/5.2/eloquent
So first of all, you need a User model (php artisan make:model User). You need to link the User modal to the right table.
//File: /laravel/app/User.php
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
protected $table = 'tbluser';
public function userRoles() {
//Get the table data with the user roles
$this->hasMany('App\UserRoles', 'user_id', 'id');
//First argument gets the UserRoles Modal
//Second argument get the UserRoles column 'user_id'
//Third argument get the User table - column id
}
//File: /laravel/app/UserRoles.php
use Illuminate\Database\Eloquent\Model;
class UserRoles extends Model
{
protected $table = 'tbllinkuserrole';
public function rank() {
//Get the table data with role
$this->hasOne('App\Roles', 'id', 'role_id');
//First argument gets the Roles Modal (table)
//Second argument get the Roles column 'id'
//Third argument get the UserRoles modal (table) - column role_id
}
//File: /laravel/app/Roles.php
use Illuminate\Database\Eloquent\Model;
class Roles extends Model
{
protected $table = 'tblroles';
}
Now if you call Auth::user()->userRoles you will get an array with all rows from table that are linked to the current user.
And when you're running this you dump all the user rank data.
foreach(Auth::user()->userRoles as $userrole) {
{{ dump($userrole) }}
{{ dump($userrole->rank) }}
}
I hope this works for you!
More information can be found on https://laravel.com/docs/5.2/eloquent
TRY THIS ONE
SELECT tbllinkuserrole.userid, tbllinkuserrole.roleid, tbluser.name
FROM tbllinkuserrole, tbluser
WHERE tbllinkuserrole.userid = tbluser.id
The mysql to get the row values as column,
SELECT U.id, U.Name, MAX(CASE WHEN R.roleid = 1 THEN R.roleid END) role_1, MAX(CASE WHEN R.roleid = 2 THEN R.roleid END) role_2 from table_c AS U LEFT JOIN table_b AS R on (U.id = R.userid) WHERE 1 GROUP BY U.id
Is there a way in Active Record to construct a single query that will do a conditional join for multiple primary keys?
Say I have the following models:
Class Athlete < ActiveRecord::Base
has_many :workouts
end
Class Workout < ActiveRecord::Base
belongs_to :athlete
named_scope :run, :conditions => {:type => "run"}
named_scope :best, :order => "time", :limit => 1
end
With that, I could generate a query to get the best run time for an athlete:
Athlete.find(1).workouts.run.best
How can I get the best run time for each athlete in a group, using a single query?
The following does not work, because it applies the named scopes just once to the whole array, returning the single best time for all athletes:
Athlete.find([1,2,3]).workouts.run.best
The following works. However, it is not scalable for larger numbers of Athletes, since it generates a separate query for each Athlete:
[1,2,3].collect {|id| Athlete.find(id).workouts.run.best}
Is there a way to generate a single query using the Active Record query interface and associations?
If not, can anyone suggest a SQL query pattern that I can use for find_by_SQL? I must confess I am not very strong at SQL, but if someone will point me in the right direction I can probably figure it out.
To get the Workout objects with the best time:
athlete_ids = [1,2,3]
# Sanitize the SQL as we need to substitute the bind variable
# this query will give duplicates
join_sql = Workout.send(:santize_sql, [
"JOIN (
SELECT a.athlete_id, max(a.time) time
FROM workouts a
WHERE a.athlete_id IN (?)
GROUP BY a.athlete_id
) b ON b.athlete_id = workouts.athlete_id AND b.time = workouts.time",
athlete_ids])
Workout.all(:joins => join_sql, :conditions => {:athlete_id => })
If you require just the best workout time per user then:
Athlete.max("workouts.time", :include => :workouts, :group => "athletes.id",
:conditions => {:athlete_id => [1,2,3]}))
This will return a OrderedHash
{1 => 300, 2 => 60, 3 => 120}
Edit 1
The solution below avoids returning multiple workouts with same best time. This solution is very efficient if athlete_id and time columns are indexed.
Workout.all(:joins => "LEFT OUTER JOIN workouts a
ON workouts.athlete_id = a.athlete_id AND
(workouts.time < b.time OR workouts.id < b.id)",
:conditions => ["workouts.athlete_id = ? AND b.id IS NULL", athlete_ids]
)
Read this article to understand how this query works. Last check (workouts.id < b.id) in the JOIN ensures only one row is returned when there are more than one matches for the best time. When there are more than one match to the best time for an athlete, the workout with the highest id is returned(i.e. the last workout).
Certainly following will not work
Athlete.find([1,2,3]).workouts.run.best
Because Athlete.find([1,2,3]) returns an array and you can't call Array.workouts
You can try something like this:
Workout.find(:first, :joins => [:athlete], :conditions => "athletes.id IN (1,2,3)", :order => 'workouts.time DESC')
You can edit the conditions according to your need.
I have two models, Post hasMany Comment. How do I select all Post that have less than two Comment?
I tried using a find with 'fields'=>array('COUNT(Comment.id) as numComments','Post.*'), (and then doing a numComments < 2 in 'conditions'). But, I get a Unknown column 'Comment.id' in 'field list' error.
Thanks!
EDIT: I've gotten CakePHP to generate this query:
SELECT `Post`.*, FROM `posts` AS `Post`
LEFT JOIN comments AS `Comment` ON (`Post`.`id` = `Comment`.`text_request_id`)
WHERE COUNT(`Comment`.`id`) < 2
GROUP BY `Comment`.`post_id`
LIMIT 10
But I get an error #1111 - Invalid use of group function on the COUNT function.
EDIT:
Resolved, use the HAVING COUNT instead of WHERE COUNT.
class Post extends AppModel
{
var $name = "Post";
var $hasMany = array('Comment'=>array('counterCache'=>true));
}
add comment_count fields into posts
an that's all :-)
In raw SQL, the query would look something like this:
SELECT Post.*
FROM Post LEFT JOIN Comment ON Post.id = Comment.post_id
GROUP BY Comment.post_id
HAVING COUNT(Comment.id) < 2
Most of these are easily translated to Cake:
array(
'having' => array('COUNT(Comment.id) <' => 2),
'group' => array('Comment.post_id')
)
Cake does not automatically join hasMany tables though, this is something you'll need to do manually. Have a look at the documentation for the details.
Edit:
You can do a having clause as follows:
array(
'group' => 'Comment.post_id HAVING COUNT(Comment.id) < 2'
)
The limitations are string only for the group and cant be done without the group by. Cake 3 will probably include more SQL syntax such as HAVING
If anyone interested:
here's the doc about counterCache http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#belongsto
and here's more about adding multiple counterCache fields http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#multiple-countercache