I have two Models(or Tables in Cakephp 3), one of them is Department and the other is Manager, I did a 'hasOne' association between those two:
Department Table (Database):
------------------------
| id | name | location |
------------------------
| 1 | IT | New York |
------------------------
Manager Table (Database):
-----------------------------------------
| id | name | address | department_id |
-----------------------------------------
| 1 | Mike | New York | 1 |
-----------------------------------------
DepartmentsTable.php
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class DepartmentsTable extends Table {
public function initialize(array $config){
}
}
ManagersTable.php
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class ManagersTable extends Table {
public function initialize(array $config){
$this->hasOne('Departments');
}
}
I created a Form so I can save a person with his address :
<?php echo $this->Form->create($manager); ?>
<?php echo $this->Form->input('Manager.name'); ?>
<?php echo $this->Form->input('Manager.address'); ?>
<?php echo $this->Form->input('Manager.department.name'); ?>
<?php echo $this->Form->input('Manager.department.location'); ?>
<?php echo $this->Form->end('valider'); ?>
and this is the controller :
<?php
namespace App\Controller\Adminocity;
use App\Controller\AppController;
use Cake\Core\Configure;
use Cake\Network\Exception\NotFoundException;
use Cake\View\Exception\MissingTemplateException;
use Cake\Cache\Cache;
use Cake\ORM\TableRegistry;
use Cake\Event\Event;
class ManagersController extends AppController {
public function edit_manager($id=null)
{
$this->loadModel('Departments');
$manager = $this->Managers->newEntity();
if($this->request->is('post')){
$managersTable = TableRegistry::get('Managers');
$manager = $managersTable->patchEntity($manager,$this->request->data(), ['associated' => ['Departments']]);
if($managersTable->save($manager)){
$this->Flash->success(__('Manager ajouté avec succès'));
return $this->redirect(['action' => 'list_managers']);
}
}
$this->set('manager', $manager);
}
}
When I try to submit the form after filling it, the two new rows of 'Departments' and 'Manager' are added successfully, but the new row of 'Manager' has the value 'NULL' on 'department_id' column instead of the value of the id of the new 'Department' row recently created.
Please, can anyone help me solve this problem ?
The relationship type should be belongsTo()
As a general rule, you can know when to use belongsTo or hasOne:
belongsTo: The foreign key is in the same table (Managers has a department_id)
hasOne: The foreign key is in the other table. (Department hasOne Manager)
Related
I have an issue with getting data of the joined table if I use query().
I did not get the data of product table, how can I solve this using query() without using Active record?
Here is my db table structure
category table
+--------------------------+
| cId | added | category |
+-----+--------+-----------+
| 1 | 1.2.20 | PC |
| 2 | 1.7.20 | electron |
+-----+--------+-----------+
product table
+--------------------------+
| id | cId | cost |
+-----+--------+-----------+
| 1 | 1 | 3000 |
| 1 | 2 | 9000 |
+-----+--------+-----------+
My Model
protected $table = 'category';
public function showProduct()
{
$sql = "SELECT
category.*, COALESCE(SUM(product.cost),0) as price
FROM category
JOIN product
ON product.cId = category.cId
GROUP BY category.cId
";
$this->db->query($sql);
return $this;
}
My Controller
public function index()
{
$result = $this->model->showProduct();
echo "<pre>";
print_r($result->asObject()->paginate(1));
//pagination
$pager = $this->model->showProduct()->pager->links();
}
Result I get
Array
(
[0] => stdClass Object
(
[cId] => 1
[added] => 1.2.20
[category] => PC
)
[1] => stdClass Object
(
[cId] => 2
[added] => 1.7.20
[category] => electron
),
)
You are requested to run this code.
SELECT category.cId,category.added,category.category,product.id,COALESCE(SUM(product.cost),0) as price
FROM category,product
WHERE category.cId=product.cId
GROUP BY category.cId;
If you are using CodeIgniter-4 then you can easily write this using Query Builder Class.
$sql="SELECT category*,product* FROM category INNER JOIN product.cid=category.id WHERE category.id";
I found solution to this by setting this table property within a function.
Model
$protected $table = array("default bd table here");
public function showProduct()
{
$this->table = array('category');
$sql = "SELECT
category.*, COALESCE(SUM(product.cost),0) as price
FROM category
JOIN product
ON product.cId = category.cId
GROUP BY category.cId
";
$this->db->query($sql);
return $this;
}
My Controller
public function index()
{
$result = $this->model->showProduct();
//pagination
$pager = $this->model->showProduct()->pager->links();
}
On terminal, in mysql , running the following query gives this result
mysql> SELECT DISTINCT(city) FROM outlets_data;
+-----------+
| city |
+-----------+
| Paris |
| New York |
| Kolkata |
| Moscow |
| Mumbai |
| Hyderabad |
| Delhi |
| Chennai |
+-----------+
8 rows in set (0.00 sec)
I want to store the names of these cities, in an array, in codeigniter 4 models class file.
Models/DashboardModels.php
<?php
namespace App\Models;
use CodeIgniter\Model;
class DashboardModel extends Model
{
protected $table = 'outlets_data';
protected $primaryKey = 'shop_id';
public function not_defined_yet()
{
$city_names = $this->select('city')->distinct(); // This should be equivalent to "SELECT DISTINCT(city) FROM outlets_data";
return $city_names;
}
}
Controller/Home.php
<?php
namespace App\Controllers;
use App\Models\DashboardModel;
use CodeIgniter\Model;
class Home extends BaseController
{
public function index()
{
$model = new DashboardModel();
$data['undefined'] = $model->not_defined_yet();
echo view('dashboard', $data);
}
}
Views/Dashboard.php
<?php echo "<pre>"; print_r($undefined); echo "</pre>"; ?>
I expect to get names of the cities in output array, but I am getting whole database as associative array.
Your function should be:
public function not_defined_yet()
{
$city_names = $this->select('city')->distinct(); // This should be equivalent to "SELECT DISTINCT(city) FROM outlets_data";
return $this;
}
Then your function be
$data['undefined'] = $model->not_defined_yet()->findAll();
Other way you can do it is loading a new instance of the database object.
public function not_defined_yet()
{
$db = \Config\Database::connect();
$builder = $db->table('outlets_data');
$city_names = $builder->select('city')->distinct();
return $city_names->resultArray();
}
You can even remove the function all together and in your controller do this:
$data['undefined'] = $model->select('city')->distinct()->findAll();
This would get the same exact result.
Here my tables, User is the classic Auth table:
DataTypes
+----+--------------+
| id | field |
+----+--------------+
| 1 | address |
| 2 | mobile_phone |
| 3 | city |
+----+--------------+
UserData
+----+----------+--------------+---------+
| id | value | data_type_id | user_id |
+----+----------+--------------+---------+
| 1 | Milan | 3 | 1 |
| 2 | 99123233 | 2 | 1 |
+----+----------+--------------+---------+
My current crazy model:
class User extends Authenticatable {
public function field($field){
$field=DataType::where('field',$field)->first();
return $this->hasMany('App\UserData')->where('datatype_id',(isset($field->id) ? $field->id : 0));
}
}
Of course it works fine when I have to find values:
auth()->user()->field('mobile_phone')->get();
But how do I set or update a new mobile phone?
Another approach is link User and DataTypes as a many to many relationship and use a scope to handle it.
First of all, change UserData table to dataTypeUser, so you could use default eloquent relationships.
In any model put the belongsToMany relationship.
User Model
public function dataTypes()
{
return $this->belongsToMany('App\DataType')
->withPivot(['value']);
}
public function scopeField($query, $field)
{
return $query->whith(['dataTypes'=>function($q)use($field){
$q->where('data_types.field',$field);
}]);
}
DataType Model
public function users()
{
return $this->belongsToMany('App\User')
->withPivot(['value']);
}
To get some value you can use $field = $user->field('something')->first() to save new data could use $user->field('something')->updateExistingPivot($field->id,['value'=>$newValue]).
Anyway, if you don't have many data of same type attached to your user (more than one phone number for example), it could be a better approach to use one single table extending user migration, or at last an userData with columns for each datatype. In small applications you have no problems but as your application grows performance will be a problem with many tables and many relationships.
To avoid a long declaration, you could overwrite magic methods __get and __set. Both are declared in Illuminate\Database\Eloquent\Model so put in your User Model:
public function __get($key)
{
return $this->getAttribute($key) ?? $this->getAttributesFromPivot($key);
}
public function __set($key, $value)
{
if(in_array($key,array_keys($this->original)))
$this->setAttribute($key, $value);
else
$this->setAttributeFromPivot($key, $value);
}
public function setAttributeFromPivot($key, $value)
{
$this->dataTypes()->where('field',$key)->update(['value'=>$value]);
}
protected function getAttributesFromPivot($key)
{
return $this->dataTypes()
->where('field',$key)
// use this to get only one value direct
->first()->pivot->value ?? null;
// or use this to get all of them as array
// ->get()
// ->map(function($item){ return $item->pivot->value;}) ?? null;
}
In this approach, you could use $user->city to get field city or another one replacing ->city. Or you could use $user->address = 'foo'; to set a new value in pivot table. Note that it will update database directly.
Now, if you are not comfortable to overwrite those methods, you don't need to. Change the signatures of setAttributeFromPivot and getAttributeFromPivot to public function getField($key) and public function setField($key, $value). Now you can use them as common methods.
I have these 2 tables:
**users**
id | name | email | ...
-----------
**rooms**
user_id | title | status | ...
---------------------------
How can i select all users's email with rooms where rooms status is Listed in Laravel 5 ?
What you have to do, you have to use "with" and "has"
$user = User::with(['room'])->whereHas('room,function($query){
$q->where('status',1);
})->get();
create model of User and Room & a relation in user name room.
May be the above one solve your problem
If you have already define a relation between App\User and App\Room Models like this.
class User {
/* other codes in your model goes here */
public function rooms(){
return $this->hasMany(App\Room::class);
}
}
class Room {
/* other codes in your model goes here */
public function user(){
return $this->belongsTo(App\User::class);
}
}
You can retrieve all users's email with rooms where rooms status is Listed like this
$users= \App\User::with(['rooms' => function($query){
$query->where('status', 'listed');
}])->pluck('email');
I have two tables in the database :
apartments table
id | user_id | Location
agents table
id | name | email | town
I have created a relationship between the two table in their respective models as below :
Apartment Model:
public function agent(){
return $this->belongsTo('App\Agent');
}
Agent Model :
public function apartments()
{
return $this->hasMany('App\Apartment');
}
When I fill in a form to post data into the apartments table I get this error :
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user_id' cannot be null
(SQL: insert into apartments (user_id, Photo, Location, Size, Bedrooms, MasterBedrooms,
Unitdetails, Housedetails, Rent, updated_at, created_at) values (, 1497870161.jpg, Nairobi,
One bedroom, 1, 0, unit, house, 12000, 2017-06-19 11:02:42, 2017-06-19 11:02:42))
This is my controller :
class ApartmentController extends Controller
{
public function store(Request $request){
//Validating the data
$this->validate($request, array(
'Photo'=>'required|image',
'Location'=>'required|max:255',
'Size'=>'required',
'Bedrooms'=>'required',
'MasterBedrooms'=>'required',
'Unitdetails'=>'required',
'Housedetails'=>'required',
'Rent'=>'required',
));
//Store data into DB
$apartment = new Apartment;
$apartment->user_id = Auth()->id();
// $house->Photo = $request->Photo;
//Saving the photo
$image = $request->file('Photo');
$filename = time() . '.' . $image->getClientOriginalExtension();
$location = public_path('images/' . $filename);
Image::make($image)->resize(735, 460)->save($location);
$apartment->Photo = $filename;
$apartment->Location = $request->Location;
$apartment->Size = $request->Size;
$apartment->Bedrooms = $request->Bedrooms;
$apartment->MasterBedrooms = $request->MasterBedrooms;
$apartment->Unitdetails = $request->Unitdetails;
$apartment->Housedetails = $request->Housedetails;
$apartment->Rent = $request->Rent;
$apartment->save();
Session::flash('success', 'Sent successfully!');
return redirect::back();
}
}
This my migration to add foreign key to the apartments table:
public function up()
{
Schema::table('apartments', function (Blueprint $table) {
$table->integer('user_id')->after('id')->unsigned();
$table->foreign('user_id')->references('id')->on('agents');
});
}
The user_id in the apartments table should come from the agents table or should be the agent id in the agents table.
What I'm I missing?