I am using Phalcon to query some relations between models.
I have the following models:
<?php
class Client extends ModelBase
{
private $id;
private $code;
private $active;
public function initialize()
{
parent::initialize();
$this->hasManyToMany(
"id",
"ClientAddress",
"client_id",
"address_id",
"Address",
"id",
array("alias" => "addresses")
);
public function getAddresses($params = null) {
return $this->getRelated("addresses", $params);
}
}
class Address extends ModelBase
{
private $id;
.......
<?php
class ClientAddress extends ModelBase
{
private $id;
private $active;
public function initialize()
{
parent::initialize();
$this->belongsTo("client_id", "Client", "id", array(
"alias" => "client"
));
$this->belongsTo("address_id", "Address", "id", array(
"alias" => "address"
));
}
}
I am receiving the below error:
The column 'id' is ambiguous, when preparing: SELECT [Address].* FROM [Address] INNER JOIN [ClientAddress] ON [ClientAddress].[address_id] = [Address].[id] WHERE [ClientAddress].[client_id] = ?0 AND id = ?1
The code that generates this error is:
$client = Client::findFirst(array(
"conditions" => "id = ?1",
"bind" => array( 1 => $current_client_id )
));
$addresses = $client->getAddresses(array(
"conditions" => "id = ?1",
"bind" => array( 1 => $id )
));
Why is this happening? Do I not have my relations setup properly? I would have assumed that it would prefix my where clause with the table name?
I had the same problem than you. I fixed it by adding
$client = Client::findFirst(array(
"conditions" => "[ClientAddress].[id] = ?1",
"bind" => array( 1 => $current_client_id )
));
$addresses = $client->getAddresses(array(
"conditions" => "[Address].[id] = ?1",
"bind" => array( 1 => $id )
));
You have the following problem when you have the same column name of two different MySQL table
Related
Model
class ClickMeeting extends Model
{
protected $table = 'clickmeeting';
public $timestamps = false;
protected $dateFormat = 'U';
protected $guarded = ['id'];
static $videoDemoSource = ['upload', 'youtube', 'vimeo', 'external_link'];
public function ClickMeeting()
{
///
}
}
Controller
public function dashboard()
{
$client = new Client();
$uri = 'https://api.clickmeeting.com/v1/conferences/active';
$header = ['headers' => ['X-Api-Key' => 'xxxxxxxxxxxxxxxxxxxxxxxx']];
$res = $client->get($uri, $header);
$conferences = json_decode($res->getBody()->getContents(), true);
// dd($conferences);
collect($conferences)
->each(function ($conference, $key) {
ClickMeeting::firstOrCreate([
'parent_id' => $conference['parent_id'],
'room_type' => $conference['room_type'],
'room_url' => $conference['room_url'],
],
[
'starts_at' => $conference['starts_at'],
'ends_at' => $conference['ends_at'],
'room_pin' => $conference['room_pin'],
'title' => $conference['name'],
'name_url' => $conference['name_url'],
'access_type' => $conference['access_type'],
'lobby_enabled' => $conference['lobby_enabled'],
'lobby_description' => $conference['lobby_description'],
'registration_enabled' => $conference['registration_enabled'],
'status' => $conference['status'],
'timezone' => $conference['timezone'],
'timezone_offset' => $conference['timezone_offset'],
'paid_enabled' => $conference['paid_enabled'],
'automated_enabled' => $conference['automated_enabled'],
'type' => $conference['type'],
'permanent_room' => $conference['permanent_room'],
'embed_room_url' => $conference['embed_room_url']
]);
});
$conferences = ClickMeeting::get();
return view('admin.clickmeeting.dashboard',compact('conferences'));
SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime
value: '2022-06-22T16:10:00+00:00' for column 'starts_at' at row 1
(SQL: insert into clickmeeting (parent_id, room_type,
room_url, starts_at, ends_at, room_pin, title, name_url,
access_type, lobby_enabled, lobby_description,
registration_enabled, status, timezone, timezone_offset,
paid_enabled, automated_enabled, type, permanent_room,
embed_room_url) values (?, webinar,
https://abc.clickmeeting.com/urinary-tract-infection-in-children,
2022-06-22T16:10:00+00:00, 2022-06-22T17:10:00+00:00, 477736894,
URINARY TRACT INFECTION IN CHILDREN,
urinary-tract-infection-in-children, 1, 1, , 1, active, Africa/Accra,
0, 0, 0, 0, 0,
https://abc.clickwebinar.com/embed_conference.html?r=123456))
I keep getting Invalid datetime format: 1292 Incorrect datetime value. Help is greatly appreciated. Thank you
I think these occur because the DATETIME value in the statement above uses a format that is not supported by MySQL, you can use the STR_TO_DATE() function for passing the starts_at variable into the database.
The code like
'starts_at' => STR_TO_DATE($conference['starts_at'], "%m-%d-%Y %H:%i:%s"),
Please check this link you can find more about the STR_TO_DATE() function
Try use Carbon\Carbon for assigning your dates:
collect($conferences)
->each(function ($conference, $key) {
ClickMeeting::firstOrCreate([
....
],
[
'starts_at' => Carbon::parse($conference['starts_at']),
'ends_at' => Carbon::parse($conference['ends_at']),
....
]);
});
Alternatively, you could tell laravel which fields are date using the $casts property:
class ClickMeeting extends Model
{
....
protected $guarded = ['id'];
protected $casts = [
'starts_at' => 'datetime',
'ends_at' => 'datetime'
];
....
}
I have an articles table with a column tagging_id that needs to reference the id column in a table called tracking_categories as a foreign key.
My Article.php file has the following:
class Article extends AppModel {
public $controllerPath = 'articles';
public $useTable = 'articles';
var $binds = array(
'TrackingCategory' => array(
'bindType' => 'belongsTo',
'className' => 'TrackingCategory',
'foreignKey' => 'tagging_id'
),
'Channel' => array(
'bindType' => 'belongsTo',
'className' => 'Channel',
'foreignKey' => 'channel_id'
)
);
My TrackingCategory.php looks like this:
class TrackingCategory extends AppModel {
public $useTable = 'tracking_categories';
I have the following in the AppModel which Article.php inherits:
function expects($binds, $reset = false) {
if (func_num_args() > 2 || !is_bool($reset)) {
throw new InvalidArgumentException('Did you mean to pass an
array of binds?');
}
if (!is_array($binds)) {
$binds = array($binds);
}
foreach ($binds as $bind) {
if (isset($this->binds[$bind])) {
$tmp = array($this->binds[$bind]['bindType'] => array($bind => $this->binds[$bind]));
$this->bindModel($tmp, $reset);
}
}
}
I am getting the following error:
Database Error
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Article.tracking_category_id' in 'field list'
It is still looking for the column named parsed via CakePHP's naming convention rather than the name that I explicitly set via the foreignKey value. What am I missing here?
The right syntax should be:
public $belongsTo = array(
'TrackingCategory' => array(
'className' => 'TrackingCategory',
'foreignKey' => 'tagging_id',
)
);
For more information, see CakePHP 2.x - Associations: Linking Models Together - belongsTo.
In a controller I have the following code:
public function actionView($id)
{
$query = new Query;
$query->select('*')
->from('table_1 t1')
->innerJoin('table_2 t2', 't2.t1_id = t1.id')
->innerJoin('table_3 t3', 't2.t3_id = t3.id')
->innerJoin('table_4 t4', 't3.t4_id = t4.id')
->andWhere('t1.id = ' . $id);
$rows = $query->all();
return $this->render('view', [
'model' => $this->findModel($id),
'rows' => $rows,
]);
}
See the db schema: https://github.com/AntoninSlejska/yii-test/blob/master/example/sql/example-schema.png
In the view view.php are displayed data from tables_2-4, which are related to table_1:
foreach($rows as $row) {
echo $row['t2_field_1'];
echo $row['t2_field_2'];
...
}
See: Yii2 innerJoin()
and: http://www.yiiframework.com/doc-2.0/yii-db-query.html
It works, but I'm not sure, if it is the most correct Yii2's way.
I tried to define the relations in the model TableOne:
public function getTableTwoRecords()
{
return $this->hasMany(TableTwo::className(), ['t1_id' => 'id']);
}
public function getTableThreeRecords()
{
return $this->hasMany(TableThree::className(), ['id' => 't3_id'])
->via('tableTwoRecords');
}
public function getTableFourRecords()
{
return $this->hasMany(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecords');
}
and then to join the records in the controller TableOneController:
$records = TableOne::find()
->innerJoinWith(['tableTwoRecords'])
->innerJoinWith(['tableThreeRecords'])
->innerJoinWith(['tableFourRecords'])
->all();
but it doesn't work. If I join only the first three tables, then it works. If I add the fourth table, then I receive the following error message: "Getting unknown property: frontend\models\TableOne::t3_id"
If I change the function getTableFourRecords() in this way:
public function getTableFourRecords()
{
return $this->hasOne(TableThree::className(), ['t4_id' => 'id']);
}
then I receive this error message: "SQLSTATE[42S22]: Column not found: 1054 Unknown column 'table_4.t4_id' in 'on clause'
The SQL being executed was: SELECT table_1.* FROM table_1 INNER JOIN table_2 ON table_1.id = table_2.t1_id INNER JOIN table_3 ON table_2.t3_id = table_3.id INNER JOIN table_4 ON table_1.id = table_4.t4_id"
You should have to define key value pair in the relation eg:
class Customer extends ActiveRecord
{
public function getOrders()
{
return $this->hasMany(Order::className(), ['customer_id' => 'id']); // Always KEY => VALUE pair this relation relate to hasMany relation
}
}
class Order extends ActiveRecord
{
public function getCustomer()
{
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
// Always KEY => VALUE pair this relation relate to hasOne relation
}
}
Now in your forth relation use:
public function getTableFourRecords()
{
return $this->hasOne(TableThree::className(), ['id' => 't4_id']);
}
You can read more on ActiveRecord here
Based on the answer of softark the simplest solution can look like this:
Model TableOne:
public function getTableTwoRecords()
{
return $this->hasMany(TableTwo::className(), ['t1_id' => 'id']);
}
Model TableTwo:
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
Model TableThree:
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id']);
}
Controller TableOneController:
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
The view table-one/view.php:
foreach ($model->tableTwoRecords as $record) {
echo ' Table 2 >> ';
echo ' ID: ' . $record->id;
echo ' T1 ID: ' . $record->t1_id;
echo ' T3 ID: ' . $record->t3_id;
echo ' Table 3 >> ';
echo ' ID: ' . $record->tableThreeRecord->id;
echo ' T4 ID: ' . $record->tableThreeRecord->t4_id;
echo ' Table 4 >> ';
echo ' ID: ' . $record->tableThreeRecord->tableFourRecord->id;
echo ' <br>';
}
A solution based on the GridView is also possible.
Model TableTwo:
public function getTableOneRecord()
{
return $this->hasOne(TableOne::className(), ['id' => 't1_id']);
}
public function getTableThreeRecord()
{
return $this->hasOne(TableThree::className(), ['id' => 't3_id']);
}
public function getTableFourRecord()
{
return $this->hasOne(TableFour::className(), ['id' => 't4_id'])
->via('tableThreeRecord');
}
The function actionView in TableOneController, which was generated with Gii for the model TableTwo was edited:
use app\models\TableTwo;
use app\models\TableTwoSearch;
...
public function actionView($id)
{
$searchModel = new TableTwoSearch([
't1_id' => $id, // the data have to be filtered by the id of the displayed record
]);
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('view', [
'model' => $this->findModel($id),
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
and also the views/table-one/view.php:
echo GridView::widget([
'dataProvider' => $dataProvider,
'columns' => [
'id',
't1_id',
'tableOneRecord.id',
't3_id',
'tableThreeRecord.id',
'tableThreeRecord.t4_id',
'tableFourRecord.id',
],
]);
See the code on Github.
In a Yii application I display the data as a table. I use CGridView, that deals for me with (search based) data filter and sorting. It works.
Now I added an ENUM column status to the user database table. In the grid I can sort and filter this, but only by the value in the table. Well, it makes sense. But the user don't actually know, how it's saved n the database and works with (and wants to sort and filter by) labels.
Is there a way to provide sorting and filtering by custom labels for an ENUM database table column in Yii (using a CActiveRecord model and a CGridView generated data grid)?
database table
CREATE TABLE `users` (
`userid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`email` varchar(45) NOT NULL,
...
`status` enum('processed', 'waiting') NOT NULL,
PRIMARY KEY (`userid`),
UNIQUE KEY `email_UNIQUE` (`email`)
);
User model
/**
* ...
* #property string $userid
* #property string $email
* ...
* #property string $status
*/
class User extends CActiveRecord
{
public function tableName()
{
return 'users';
}
public function getFirstAndLastName(){
return CHtml::encode($this->firstname." ".$this->lastname);
}
public function rules()
{
return array(
...
array('userid, email, status', 'safe', 'on'=>'search'),
);
}
public function attributeLabels()
{
return array(
'userid' => 'Userid',
'email' => 'Email',
...
'status' => Yii::t('app', 'status'),
);
}
public function relations()
{
return array(
...
);
}
public function search()
{
$criteria=new CDbCriteria;
$criteria->compare('userid',$this->userid,true);
$criteria->compare('email',$this->email,true);
...
$criteria->compare('status',$this->status,true);
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
"pagination"=>array(
"pageSize"=>25
)
));
}
public static function model($className=__CLASS__)
{
return parent::model($className);
}
protected function beforeSave()
{
return parent::beforeSave(); // TODO: Change the autogenerated stub
}
public function getProcessingStatus()
{
return Yii::t('app', $this->processing_status);
}
}
UserController
class UserController extends Controller
{
...
/**
* Manages all models.
*/
public function actionAdmin()
{
// Yii::app()->cache->flush();
$model=new User('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['User']))
$model->attributes=$_GET['User'];
$this->render('admin',array(
'model'=>$model,
));
}
...
}
view
...
<?php
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'user-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'email',
'status',
array(
'class' => 'CButtonColumn',
'template' => '{view}{update}',
'buttons' => array(
'view' => array(
)
),
),
),
));
Model:
class User extends CActiveRecord {
const STATUS_PROCESSED = 'processed';
const STATUS_WAITING = 'waiting';
public static function getStatusList(){
return array(
self::STATUS_PROCESSED => 'Processed',
self::STATUS_WAITING => 'Waiting',
);
}
public function getStatusValue(){
$list = self::getStatusList();
return array_key_exists( $this->status, $list ) ? $list[ $this->status ] : 'Undefined';
}
}
View:
$this->widget('zii.widgets.grid.CGridView', array(
// other params
'columns' => array(
// other columns
array(
'name' => 'status',
'value' => '$data->getStatusValue()',
'filter' => User::getStatusList(),
),
)
));
That's all
You can specify a custom filter in your CDataColumn, by setting the filter property to be an array. It will result in a dropdown list filter instead of a text field filter.
Example:
<?php
$this->widget('zii.widgets.grid.CGridView', array(
'id' => 'user-grid',
'dataProvider' => $model->search(),
'filter' => $model,
'columns' => array(
'email',
array(
'name' => 'status',
'filter' => array(
'processed' => 'Processed',
'waiting' => 'Waiting',
),
),
array(
'class' => 'CButtonColumn',
'template' => '{view}{update}',
'buttons' => array(
'view' => array(
)
),
),
),
));
Also check out the docs
I'm using the default Zend_Db_Table_Abstract to set the relationships. These are my models:
Model_Table_Project:
protected $_name = 'projects';
protected $_dependentTables = array('Model_Table_ProjectStage');
protected $_referenceMap = array(
'User' => array(
'columns' => 'userId',
'refTableClass' => 'Model_Table_User',
'refColumns' => 'id'
)
);
public function deleteProject($id)
{
$row = $this->find($id)->current();
if ($row)
{
$row->delete();
}
else
{
throw new Zend_Exception('Cannot delete project.');
}
}
Model_Table_Stage:
protected $_name = 'stages';
protected $_dependentTables = array('Model_Table_ProjectStage');
Model_Table_ProjectStage:
protected $_name = 'projectstage';
protected $_referenceMap = array(
'Project' => array(
'columns' => 'projectId',
'refTableClass' => 'Model_Table_Project',
'refColumns' => 'id',
'onDelete' => self::CASCADE,
),
'Stage' => array(
'columns' => 'stageId',
'refTableClass' => 'Model_Table_Stage',
'refColumns' => 'id',
'onDelete' => self::CASCADE,
)
);
Now when I want to delete a project, using the deleteProject() method in Model_Table_Project, it deletes the entry in the Project table and the entries in the ProjectStage table, but it doesn't delete the entries in the Stage table.
What am I doing wrong?
I've found the solution. It was a thinking error of myself. The solution I now use is:
In the Table model I call $row->delete() and in the Row model I declared a delete method:
public function delete()
{
$stages = $this->getStages();
foreach ($stages as $stage)
{
$stage->delete();
}
return parent::delete();
}
This way it first delete all relating stages and then it deletes itself.