Cake PHP - how to check the condition only for the last - php

I am following instructions from Bookmarks tutorial, and i have a problem with one of the queries.
I have baked all models from my database (like in tutorial) and now i want to prepare custom finder.
I have two tables Academic Teachers and Evaluations
CREATE TABLE evaluations (
ID int(10) NOT NULL AUTO_INCREMENT,
academic_teacher_ID int(10) NOT NULL,
framework_ID int(10) NOT NULL,
protocol_ID int(10) NOT NULL,
final_note DOUBLE,
room varchar(255),
PRIMARY KEY (ID)) ;
CREATE TABLE academic_teachers (
ID int(10) NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
surname varchar(255) NOT NULL,
national_identification_number int(10) NOT NULL,
occupation varchar(4),
degree varchar(255),
department varchar(255),
PRIMARY KEY (ID)) ;
I want to find teachers, who at their last evaluation received a low note.
public function findLowNotes(Query $query, array $options)
{
return $this->find()
->distinct(['AcademicTeachers.id'])
->matching('Evaluations', function ($q) {
return $q->where(['Evaluations.final_note' < 3]);
});
But it wil find all the teachers who ever had any bad note. How should it be? Shall I somewhere use one of the ways of retrieving last ID that I found? Or there is a more clever way that will surely work? I am much confused how to combine it all together.
And - is it possible to join here with that final_note, so I could paginate it along with that teacher's data?
Yours sincerely,
Milven

This is not right.
return $q->where(['Evaluations.final_note' < 3]);
It should be.
return $q->where(['Evaluations.final_note <' => 3]);

Related

Laravel Eloquent ManyToMany with Pivot, secondary query with variables in pivot table?

I've been playing with laravel a bit and came across a weird edge case I can't quite figure out
I've got the following table structure:
CREATE TABLE `community_address` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`address_id` int(10) unsigned NOT NULL,
`community_id` int(10) unsigned NOT NULL,
`is_billing` tinyint(1) NOT NULL DEFAULT '1',
`is_service` tinyint(1) NOT NULL DEFAULT '1',
`is_mailing` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
)
CREATE TABLE `communities` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
)
CREATE TABLE `addresses` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`address_1` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Street address',
`address_2` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Street adddress 2 (Company name, Suite, etc)',
`city` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'City',
`state` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'State / Province',
`zip` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Zip / Postal Code',
`country_id` int(10) unsigned NOT NULL COMMENT 'Country ID',
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
)
Which i've represented with the following Laravel Model for a community
class Community extends Model
{
public function addresses(){
return $this->belongsToMany(Address::class, 'community_address', 'community_id', 'address_id');
}
}
$community->addresses() does in fact return only addresses for the community, but say I want to filter by address type in my pivot table (billing, mailing, etc)
I can try this:
public function getBillingAddress(){
return $this->addresses()->wherePivot('is_billing','=', true)->firstOrFail()->get();
}
Which does return results, however it's EVERY row in my pivot table matching my query, not running my query off the existing addresses
So my second idea was to use the 'and' boolean argument like so
public function getBillingAddress(){
return $this->addresses()->wherePivot('community_id', '=', $this->id, true)->wherePivot('is_billing','=', true)->firstOrFail()->get();
}
Which results in the following SQL which errors out (for obvious reasons), but also doesn't quite look like it's searching for what i'd want, even if it did work?
select `addresses`.*, `community_address`.`community_id` as `pivot_community_id`, `community_address`.`address_id` as `pivot_address_id` from `addresses` inner join `community_address` on `addresses`.`id` = `community_address`.`address_id` where `community_address`.`community_id` = 2 1 `community_address`.`community_id` = 2 and `community_address`.`is_billing` = 1 limit 1
Which looks to me like the "and" value is not, in fact, a boolean value, but is printing the value as a string straight to the query.
I tried the obvious, and tried to swap the forth argument with "and" and the following sql was generated, which doesn't fail, but returns all addresses, not just addresses linked to my community
select `addresses`.*, `community_address`.`community_id` as `pivot_community_id`, `community_address`.`address_id` as `pivot_address_id` from `addresses` inner join `community_address` on `addresses`.`id` = `community_address`.`address_id` where `community_address`.`community_id` = 2 and `community_address`.`community_id` = 2 and `community_address`.`is_billing` = 1 limit 1)
Am I missing something obvious here?
With some tinkering with the result SQL I can get what I want, which is the following raw sql query:
select `addresses`.*,
`community_address`.`community_id` as `pivot_community_id`,
`community_address`.`address_id` as `pivot_address_id`
from `addresses`
inner join `community_address` on `addresses`.`id` = `community_address`.`address_id` and `community_address`.`community_id` = 2 and `community_address`.`is_billing` = 1
limit 1
How can I achieve the same SQL being generated for me via eloquent?
I think This will be userfull For you If I come up with an example
we have Users Role And Role_User Tables
we have connect Users To Role with belongs To Many And we want use select:
Users models:
function Roles()
{
return $this->belongsToMany('App\Role', 'role_user', 'user_id', 'role_id');
}
in Our Controller we can write any select like bellow:
class exampleController extends Controller
{
public function index()
{
User::with(['Roles'=>function($query){$query->where(....)->get();}])->get();
}
}
you can Use any select on query and return what ever you want..
just be carefull if you need to use any varible in your select you must use
bellow format
class exampleController extends Controller
{
public function index()
{
$var =...;
User::with(['Roles'=>function($query) use ($var){$query->where(....,$var)->get();}])->get();
}
}
i hope this will solve your problem...
It seems I misunderstood how wherePivot() worked, changing the code to the following worked:
public function getBillingAddress(){
return $this->addresses()->wherePivot('is_billing', '=', true)->get()->first->all();
}
Where the new code is trying to call the is_billing column of the pivot table to further filter the existing table, the old one was trying to filter it by what it was already filtered by, but since it was an inner join, it was returning all the rows (At least I think?)
Either way, this is solved, hope this helps someone in the future.

Eloquent BelongsToMany Relationship searches for is null

I have 3 tables artist, catalog & cat_artist (pivot table). When retrieving a row from my catalog table I want to get all the associated artists with it. I could just do this with raw sql but since using laravel it feels wrong because it has many of the functions to do this already. So I have a the function in my product model (product modal references the catalog table) to create the join
public function artists()
{
return $this->belongsToMany('App\Artist', 'cat_artist', 'LOOK_UP_TO_CAT_ID', 'LOOK_UP_TO_ARTIST_ID');
}
I then call this before I return my view which gives me the the row from my catalog table
$product = Product::find($id);
Now I want to get all the artists that may belong to this product aswell so I call the following
$artists = $product->artists()->get();
This returns an emtpy result set
Here is the query log from the above
array(2) {
[0]=> array(3) {
["query"]=> string(60) "select * from `catelogue` where `catelogue`.`id` = ? limit 1"
["bindings"]=> array(1) {
[0]=> int(96033)
}
["time"]=> float(0.91)
}
[1]=> array(3) {
["query"]=> string(289) "select `artist`.*, `cat_artist`.`LOOK_UP_TO_CAT_ID` as `pivot_LOOK_UP_TO_CAT_ID`, `cat_artist`.`LOOK_UP_TO_ARTIST_ID` as `pivot_LOOK_UP_TO_ARTIST_ID` from `artist` inner join `cat_artist` on `artist`.`id` = `cat_artist`.`LOOK_UP_TO_ARTIST_ID` where `cat_artist`.`LOOK_UP_TO_CAT_ID` is null"
["bindings"]=> array(0) { }
["time"]=> float(0.37) } }
This is all happening before I return the view and pass the data to the view
Everything else in the query is right apart from this where it's looking for null
where `cat_artist`.`LOOK_UP_TO_CAT_ID` is null
It should be this
where `cat_artist`.`LOOK_UP_TO_CAT_ID` = ?
Please help. I can't figure out where I have gone wrong
EDIT
Here are my DB tables - watered down as showing all cols would be a waste
CREATE TABLE IF NOT EXISTS `artist` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`ARTIST` varchar(255) DEFAULT NULL,
PRIMARY KEY (`ID`),
KEY `ARTIST` (`ARTIST`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
CREATE TABLE `catelogue` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`CAT_NO` varchar(255) DEFAULT NULL,
`TITLE` varchar(255) DEFAULT NULL,
`BARCODE` varchar(15) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `BARCODE` (`BARCODE`),
UNIQUE KEY `CAT_NO` (`CAT_NO`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `cat_artist` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`LOOK_UP_TO_CAT_ID` int(11) NOT NULL,
`LOOK_UP_TO_ARTIST_ID` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`ID`),
UNIQUE KEY `unique_index` (`LOOK_UP_TO_CAT_ID`,`LOOK_UP_TO_ARTIST_ID`),
KEY `LOOK_UP_TO_CAT_ID` (`LOOK_UP_TO_CAT_ID`),
KEY `LOOK_UP_TO_ARTIST_ID` (`LOOK_UP_TO_ARTIST_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Hopefully an answer can be found as to why it is putting is null in the where statement rather than the product ID
Also I have tested the resulting raw SQl by replacing is null with a product id and it does work in phpMyAdmin so the SQL been generated is correct up to the is null
This may sounds stupid, but I think it's beacuse your field names are in uppercase letters. so laravel cannot find the id column in attributes list in Project model. so it assumes you are search for null ids.
So at least rename ID columns to id(lowercase).
After lots of digging I found that I was using the wrong var to set the $primaryKey. I was using $primary_id. How or where I picked this up I don't know but it seems to be the root cause because I wasn't changing the primaryKey var but rather declaring a new one - Stupid user error
To Any one else finding this - double check your code.
Make sure you are assigning the correct variable.
Wrapping a pre tag around my var_dump helps to read a large obeject/array and helped me to find the issue

Yii joining two table using relations in model

Hi I have these two tables that I want to join using relations in Yii, The problem is Im having a hard time figuring out how Yii relation works.
picturepost
id
title
link_stat_id
linkstat
id
link
post_count
I also have a working SQL query. This is the query I want my relation to result when I search when I want to get picturepost
SELECT picturepost.id, picturepost.title,linkstat.post_count
FROM picturepost
RIGHT JOIN linkstat
ON picturepost.link_stat_id=linkstat.link;
I want something like this when I search for a post.
$post = PicturePost::model() -> findByPk($id);
echo $post->linkCount;
Here's my table for extra info:
CREATE TABLE IF NOT EXISTS `picturepost` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`title` text COLLATE utf8_unicode_ci DEFAULT NULL,
`link_stat_id` char(64) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=MyISAM;
CREATE TABLE IF NOT EXISTS `linkstat` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`link` char(64) COLLATE utf8_unicode_ci NOT NULL,
`post_count` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
KEY `post_count` (`post_count`),
KEY `link_stat_id` (`link`)
) ENGINE=InnoDB;
Thanks in advance I hope I explained it clearly.
There are a few tutorial regarding this, and I won't repeat them, but urge you to check them out.
The easiest starting point will be to create your foreign key constraints in the database, then use the Gii tool to generate the code for the model, in this case for the table picturepost.
This should result in a class Picturepost with a method relations(),
class Picturepost extends {
public function relations()
{
return array(
'picturepost_linkstats' => array(self::HAS_MANY,
'linkstat', 'link_stat_id'),
);
}
This links the 2 tables using the *link_stat_id* field as the foreign key (to the primary key of the linked table).
When you are querying the table picturepost, you can automatically pull in the linkstat records.
// Get the picturepost entry
$picturepost = PicturePost::model()->findByPk(1);
// picturepost_linkstats is the relationship name
$linkstats_records = $picturepost->picturepost_linkstats;
public function relations()
{
return array(
'linkstat' => array(self::HAS_ONE, 'Linkstat', array('link_stat_id'=>'link')),
);
}
More on yii relations.
This assumes that you have an active record model Linkstat that represents data in table linkstat.

mysql 'likes' structure table

I'm working on a web site where users can post articles with this table structure :
CREATE TABLE IF NOT EXISTS `articles` (
`id_articles` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_users` int(10) unsigned NOT NULL,
`articles` text NOT NULL,
PRIMARY KEY (`id_articles`),
UNIQUE KEY `id_articles` (`id_articles`),
KEY `id_users` (`id_users`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Each user can 'like' the articles.
Is that the right way below to create a 'like table' :
CREATE TABLE IF NOT EXISTS `articles_likes` (
`id_articles` int(10) unsigned NOT NULL,
`id_users` int(10) unsigned NOT NULL,
KEY `id_articles` (`id_articles`,`id_users`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
It is correct but you will want to add separte indexes on id_articles and id_users (also you might want to name the columns 'id_article' and 'id_user' for sanity).
CREATE TABLE IF NOT EXISTS `article_likes` (
`id_article` int(10) unsigned NOT NULL,
`id_user` int(10) unsigned NOT NULL,
KEY `id_article` (`id_article`),
KEY `id_user` (`id_user`)
) ENGINE=InnoDB;
The reason you want separate indexes is because in mysql if you create an index on columns (A, B) that index will be used in queries having in the where clause column A, or columns A and B.
In your case for example if you made a query "SELECT * FROM article_likes WHERE id_user=X" this query would not use an index.
An ever better option would be to add a combined index and a separate index on the second column from the combined index. Like this:
CREATE TABLE IF NOT EXISTS `article_likes` (
`id_article` int(10) unsigned NOT NULL,
`id_user` int(10) unsigned NOT NULL,
KEY `id_article_user` (`id_article`, `id_user`),
KEY `id_user` (`id_user`)
) ENGINE=InnoDB;
This way you would have optimal performance on queries like 'WHERE id_user=X', "WHERE id_article=X', "WHERE id_article=X AND id_user=Y"
This is a valid way Chris. You can use COUNT() to match the id_articles in the articles_likes table against the current article you are viewing in articles.
$articles_id = 23;
mysql_query("SELECT COUNT(*) FROM articles_likes
WHERE id_articles = ".$articles_id);
You can also just leave COUNT() (MySQL) out and instantly know which users are the "likers" of the articles and use count() (PHP) on the returned Array to duplicate the effect of COUNT() in MySQL.
i would have a total of 3 tables. an articles table, and the user id could be a column in that for users who submit articles , but you need a separate user table since not all users will submit articles (i am assuming), and then a 3rd table for likes, that takes the primary key from users and the primary key from articles and uses them as foreign keys. so each time an article is liked, an entry is made in the 3rd table

CodeIgniter DataMapper ORM v1.8.0 - How to Get records from multiple tables

I am new to this and need a little help. I have a 2 tables...
CREATE TABLE `vehicles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`vehicle_type` varchar(50) NOT NULL,
`vehicle_make` varchar(50) NOT NULL,
`vehicle_model` varchar(50) NOT NULL,
`vehicle_year` varchar(50) NOT NULL,
`vin` varchar(50) NOT NULL,
`registered_state` varchar(10) NOT NULL,
`license_plate` varchar(20) NOT NULL,
`insurrance_policy` varchar(50) NOT NULL,
PRIMARY KEY(`id`)
)
ENGINE=INNODB;
CREATE TABLE `drivers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(50) NOT NULL,
`last_name` varchar(50) NOT NULL,
`dob` date NOT NULL,
`ss_no` varchar(50) NOT NULL,
`address` varchar(100) NOT NULL,
`city` varchar(50) NOT NULL,
`state` varchar(10) NOT NULL,
`zip_code` int(5) NOT NULL,
`cell_phone` varchar(50) NOT NULL,
`home_phone` varchar(50),
`dl_no` varchar(50) NOT NULL,
`dl_state` varchar(10) NOT NULL,
`dl_exp` date NOT NULL,
`dl_2_no` varchar(50) NOT NULL,
`dl_2_state` varchar(10) NOT NULL,
`dl_2_exp` date NOT NULL,
`vehicle_id` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY(`id`),
CONSTRAINT `Ref_01` FOREIGN KEY (`vehicle_id`)
REFERENCES `vehicles`(`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION
)
ENGINE=INNODB;
SET FOREIGN_KEY_CHECKS=1;
As you can see every driver has a car associated with him. How can I query every driver and every car that is associated with him in an object.
I can get all the drivers using this.
$d = new Driver();
$data['driver'] = $d->get();
In my model for driver has
var $has_one = array('vehicle');
I want to get all the records in $data['driver']
Here's one simple way, assuming you have one-to-one relationship:
In your Model Driver:
var $has_one = array('vehicle');
In your Model Vehicle:
var $has_one = array('driver');
To get a vehicle and its driver:
$v = new Vehicle();
$v->include_related('driver')->get();
include_related() will only work with $has_one related models.
Driver's properties are now stored in $v with the prefix driver_. To access the vehicle's driver columns:
echo $v->driver_first_name;
echo $v->driver_last_name;
Alternatively, you can auto-load the driver every time you access the vehicle:
// In Vehicle
var $auto_populate_has_one = TRUE;
For has-many relationships, you can do this:
$d = new Driver();
$d->get_by_id($id);// Get a driver by id
foreach ($d->vehicle->get() as $car)
{
// Print all this Driver's vehicles
echo $car->vehicle_model;
}
This is just one way to do it. There are so may ways to access relationships in Datamapper, your best bet is to read the documentation thoroughly, then read it again. Datamapper is a great ORM and you should learn and experiment with all it's features to get the most out of it.
This could be a good opportunity to use a Foreign Key in your Drivers Table. Here is a link to the MySQL documentation on using Foreign Keys in your table: MySQL Foreign Keys
Alternatively if you don't want to deal with the hassles of advanced MySQL calls you could also just process two queries, one to your vehicles table, and one to your drivers table, and then use PHP to iterate (loop) through the results until you can find and match the correct values with each other.
Actually both ways are probably a bit of a hassle to set up, but your foreign key would likely be the easier to maintain.

Categories