CakePHP 3.x Query many Table objects with Pagination - cakephp

Context:
I'm working with CakePHP 3.x and I'm trying to build an ORM query with pagination. I have the tables Users, Topics and Areas. User can have many topics but just one area. This is the db model:
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
);
CREATE TABLE topics (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
name VARCHAR(255),
description TEXT,
FOREIGN KEY user_key (user_id) REFERENCES users(id)
);
CREATE TABLE areas (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
UNIQUE KEY (title)
);
CREATE TABLE users_areas (
user_id INT NOT NULL,
area_id INT NOT NULL,
PRIMARY KEY (user_id, area_id),
FOREIGN KEY area_key(area_id) REFERENCES areas(id),
FOREIGN KEY user_key(user_id) REFERENCES users(id)
);
What I need
I need to get all the Topics filtered by an area given.
I'm trying the next:
//1. get the area from the url (OK)
$area = $this->request->getQuery('area');
//2. Get the area from db to get the id
$myarea = $this->Areas->find()->where(['title' => $area])->first();
//3. Get all the users related to the area given
$users = $this->Users->find()->matching('Areas', function ($q){
return $q->where(['Areas.id' => $myarea->id]);
});
//4. Get the Topics and Paginate
$topics = $this->paginate($this->Topics->find('all')->innerJoinWith('Users', function ($q){
return $q->where(['User.id' => $myarea->id]);
}));
//5. Set the topics (OK)
$this->set(compact('topics'));
$this->set('_serialize', ['topics']);
I got tons of errors and I'm not sure how to build the query properly.

At first glance, there's at least one mistake:
$myarea is invisible inside anonymous function.
If You wanna use $myarea inside lambda function, You should inherit this variable from parent scope: use($var).
http://php.net/manual/en/functions.anonymous.php example #3
$users = $this->Users->find()->matching('Areas', function ($q) use ($myarea) {
return $q->where(['Areas.id' => $myarea->id]);
});
$topics = $this->paginate($this->Topics->find('all')->innerJoinWith('Users', function ($q){
return $q->where(['User.id' => $myarea->id]);
}));

Related

How to get primary key name on laravel query builder

Is there a way to get a table's primary key name using the Laravel query builder? Can i do something like
$key = DB::table($someTable)->getPrimaryKey();
I've looked through the documentation and API but couldn't find any reference to it.
Thanks in advance.
You can get an array of indexes for a table directly using the Schema manager:
$indexes = DB::connection()->getDoctrineSchemaManager()->listTableIndexes($table);
OR
$indexes = Schema::getConnection()->getDoctrineSchemaManager()->listTableIndexes($table);
And then you can get an array of the columns associated with that index key:
$columns = $indexes[ 'primary' ]->getColumns();
This function can be used to find the primary key(s) or unique key(s) for a table you know nothing about:
public function getTablePrimaryOrUniqueKey($table, $key='') {
//get the array of table indexes
$indexes = DB::connection()->getDoctrineSchemaManager()->listTableIndexes($table);
//abort if there are no indexes
if(!is_array($indexes)) return false;
//set up the default array of index keys
$keys = ['primary', 'unique'];
//if a key was passed and it is valid, use it...but cast as an array
if($key!='' && in_array($key, $keys)) $keys = (array) $key;
//loop through the keys array
foreach ( $keys as $key ) {
//keep it honest
if ( array_key_exists( $key, $indexes ) ) {
//get the columns for this key
$columns = $indexes[ $key ]->getColumns();
//if we have an array of columns, we have what we need
if ( is_array( $columns ) ) {
return $columns;
}
}
}
//if you got here, you did not get find what you were looking for
return false;
}
The Illuminate/Database/Eloquent/Model class has a getKeyName function which is public.
Inside your model class, you can access the primary key using $this->getKeyName().
my approach is :
$result = DB::select(DB::raw("SHOW KEYS FROM {$table} WHERE Key_name = 'PRIMARY'"));
$primaryKey = $result[0]->Column_name;
and i use mysql
You can use ->getKey() on a model:
$someModel->getKey();
Or if you're just trying to get the name of the column, without having an instance of the model:
app(App\Model::class)->getKeyName()
$primary_key = app($builder->getModel())->getKeyName();
if you referenced the model when you initialized that Query Builder instance you can retrieve the model reference then use it to get the keyName. but if your just directly called a new Query Builder there isn't a way to see the primary key without doing a select against the table keys, maybe in a subselect?
SHOW KEYS FROM table WHERE Key_name = 'PRIMARY'

Yii2 return single value of attribute on condition

I have these tables in the database:
user : userid (primary Key), professionid, etc,
profession : professionid (primary Key), classid, etc,
class : classid (primary key), etc,
In model of User, I create function,
public function getClassid()
{
return 1;
}
How can I replace 1 to value of classid in table of profession, where userid is current logged user?
Use
return model->profession->classid
This should work
public function getClassid()
{
$profession = Profession::findOne(['professionid' = > $this->profession_id])
return $profession->classid;
}
This returns the value of the attribute classid which is stored in the table Profession where the professionid is the same and the professionid of the current User model.
Hope this helps

yii2 string validation using substr() in comparing attributes of two related model

I have this two table:
Region table
create table if not exists Region (
id int(2) unsigned not null auto_increment,
name varchar(40) unique not null,
code varchar(9) not null,
primary key(id)
);
Province table:
create table if not exists Province (
id int(2) unsigned not null auto_increment,
region_id int(2) unsigned not null,
name varchar(40) unique not null,
code varchar(9) not null,
primary key(id),
key region_id(region_id),
constraint province_region_id foreign key(region_id) references region(id)
);
sample data:
region_code: 010000000
province_code: 010100000
they are related coz they have the same first two character
ive created a partial custom validation but i cant finish it and even don't know if using substr() with arrayhelper combination is okay
public function modelCodeValidator()
{
$regCode=>substr([Arrayhelper::map(Region::find()->all()->,'id','code')],2);// specifying first two character of region.code
$provCode=>substr('code',2);//specifying first two character of province.code
return [$regCode=$provCode];
}
on this custom validation, rule() would be:
['code','compare','modelCodeValidator' don't know next]
first two character of region_code should match/equal to the first two character of province_code. by doing this user will notified that they could only enterered province code belonging to a particular region.please help
Try by something like this..
public function rules()
{
return [
['code', 'modelCodeValidator'],
];
}
public function modelCodeValidator($attribute, $params)
{
// Write your coding stuff here
if (YourModelName::findOne(['your_model_field'=> $attribute]) {
$this->addError($attribute, \Yii::t('view', 'The fields don't match.'));
}
}
take a look here :->
http://www.yiiframework.com/doc-2.0/guide-input-validation.html#client-side-validation

One to Many relationship using breezejs

I have the following one to many relationship setup between two tables (Case and Recipient)
Case table has one primary key which is identified as an identity (auto-increment) tblRecipient has one primary key which is identified as an identity (auto-increment),and a foreign key relationship with Case.
Case table and related tblRecipient data is pulled:
function searchCases(caseID, caseNum)
{
var qPredicate;
if (caseID != '')
{
qPredicate = new breeze.Predicate("pkCaseID", "==", parseInt(caseID));
}
else if (caseNum != null)
{
qPredicate = new breeze.Predicate("CaseNumber", "Contains", caseNum);
}
var query = breeze.EntityQuery
.from("case")
.where(qPredicate)
.expand("tblRecipients")
return manager.executeQuery(query);
}
When a button is pressed to add a new recipient, the following code is used to create a new recipient entity:
function createNewRecipient(CaseID)
{
var recipientEntityType = manager.metadataStore.getEntityType("tblRecipient");
var newRecipient = manager.createEntity(recipientEntityType, {fkCaseID: CaseID});
return newRecipient;
}
This code returns this error:
Error: Cannot attach an object to an EntityManager without first setting its key or setting its entityType 'AutoGeneratedKeyType' property to something other than 'None'
The AutoGeneratedKeyType in the metadata shows None instead of Identity as in the Case table. We are not sure what we need to change or how to really debug this. Any help would be appreciated.

MVC: The INSERT statement conflicted with the FOREIGN KEY constraint

I have two tables that are supposed to be related.
Tables and Coloums Specification
Primary key table
ProductCategory
ProductCategoryID
Foreign key table
SubProductCategory2
ProductCategoryID
In the controller I have the following methods when creating sub category...
public ActionResult Create()
{
ViewBag.ProductCategory = db.ProductCategories.OrderBy(p =>
p.ProductCategoryID).ToList();
ViewBag.SubProductCategory2 = db.SubProductCategory2.OrderBy(a =>
a.ProductCategoryID).ToList();
var PC2 = new SubProductCategory2();
return View(PC2);
}
public ActionResult Create(SubProductCategory2 Createsubcat2,
FormCollection values)
{
if (ModelState.IsValid)
{
db.AddToSubProductCategory2(Createsubcat2);
db.SaveChanges();
//error pointing here and the full error message I am getting is...
/*error: System.Data.SqlClient.SqlException:
* The INSERT statement conflicted with the FOREIGN KEY constraint
* "FK_SubProductCategory2_ProductCategory". The conflict occurred in
* database "MyHouseDB", table "dbo.ProductCategory", column
* 'ProductCategoryID'. The statement has been terminated.*/
return RedirectToAction("/");
}
ViewBag.ProductCategory = db.ProductCategories.OrderBy(p =>
p.ProductCategoryID).ToList();
ViewBag.SubProductCategory2 = db.SubProductCategory2.OrderBy(a =>
a.ProductCategoryID).ToList();
return View(Createsubcat2);
}
ViewBag.ProductCategory = db.ProductCategories.OrderBy(p =>
p.ProductCategoryID).ToList();
ViewBag.SubProductCategory2 = db.SubProductCategory2.OrderBy(a =>
a.ProductCategoryID).ToList();
return View(Createsubcat2);
in the views I have the following code...
<div class="editor-label">
#Html.LabelForModel()
</div>
<div class="editor-field">
#Html.DropDownList("CategoryName", new
SelectList((System.Collections.IEnumerable)ViewData["ProductCategory"],
"ProductCategoryID", "CategoryName"))
#Html.ValidationMessageFor(model => model.ProductCategory.CategoryName)
Could some tell me how to solve the The INSERT statement conflicted with the FOREIGN KEY constraint error message. Correct me if I'm wrong, have I created the relationship between two tables incorrectly or the problem else where? Thanks in advance.
This error happens when the following conditions are true 1) The value that you selected for "ProductCategoryID" is not present in the "ProductCategory" table OR 2) The product category table is empty.
Does you have values in the product category table?
What value are you choosing for ProductCategoryID?
I have found the problem. It was my sql database design not MVC coding side. I removed CategoryName column from SubCategory table. I could have felt the CategoryName as long as Allow Null was set to true. There was no point in doing that has PrimaryKey had been setup correctly for the two tables.

Resources