Models and Pivot Tables - phalcon

I have a custom written solution, which I'm porting over to Phalcon. This is my first run with Phalcon, and, though it's easy to use and well documented, I can't seem to find anything about linking models that are linked through a pivot table.
Here's the run down:
I have users. Users have stats. The two are linked with a table called users_stats. The table contains two columns: a user ID and a stat ID. I created a third class called Users_Stats to model after the pivot table.
User Model:
<?php
class Users extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasMany('stat_id', 'Users_Stats', 'user_id', array('foreignKey' => true));
}
}
Stats Model:
<?php
class Stats extends \Phalcon\Mvc\Model {
public function initialize() {
$this->belongsTo('stat_id', 'Users_Stats', 'id');
}
}
Users_Stats Model:
<?php
class Users_Stats extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasMany('user_id', 'Users', 'id');
$this->hasMany('stat_id', 'Stats', 'id');
}
}
I just want to be able to gather the stats based on users. I am not sure if all I'm missing is that each of the models needs to belong and hasMany (Stats has many User_Stats && User_Stats has many Stat; etc.). I know I'm missing something though.
Any help would be appreciated! Thanks!

Try this (source):
User Model:
<?php
class Users extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasMany(
'stat_id',
'Users_Stats',
'user_id',
array('foreignKey' => true)
);
}
}
Stats Model:
<?php
class Stats extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasMany('stat_id', 'Users_Stats', 'id');
}
}
Users_Stats Model:
<?php
class Users_Stats extends \Phalcon\Mvc\Model {
public function initialize() {
$this->belongsTo('user_id', 'Users', 'id');
$this->belongsTo('stat_id', 'Stats', 'id');
}
}
User 1->many Users_Stats many<-1 Stats

After some playing around, and some great help from Nikolaos Dimopoulos, here's something things I found out.
Classes can't have underscores.
A lookup table's model must belong to each of the classes, as well as must have many of those same classes.
Each class belonging to a lookup table's model must belong to the lookup table's model, as well as must have many of the lookup table's model.
User Model:
<?php
class Users extends \Phalcon\Mvc\Model {
public function initialize() {
$this->belongsTo('id', 'UserStats', 'user_id');
$this->hasMany('user_id', 'UserStats', 'id');
}
}
Stat Model:
<?php
class Stats extends \Phalcon\Mvc\Model {
public function initialize() {
$this->hasMany('stat_id', 'UserStats', 'id');
$this->belongsTo('stat_id', 'UserStats', 'id');
}
}
UserStats Model:
<?php
class UserStats extends \Phalcon\Mvc\Model {
/**
* Set database name as there is no UserStats DB
*
* #return string
*/
public function getSource() {
return 'user_stats';
}
public function initialize() {
$this->belongsTo('user_id', 'Users', 'id');
$this->belongsTo('stat_id', 'Stats', 'id');
$this->hasMany('id', 'Users', 'user_id');
$this->hasMany('id', 'Stats', 'stat_id');
}
}

Related

Property [client] does not exist on this collection instance

I can't get the value of the foreign key that is inside the booking table.
Booking Table
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class booking extends Model
{
protected $primaryKey = 'bookingID';
protected $fillable = ['clientID', 'checkInDate', 'checkOutDate', 'roomsCount', 'roomTypeID', 'adultsCount', 'childrenCount', 'amenityID', 'paymentID'];
public function client()
{
return $this->hasOne(Client::class,'clientID');
}
}
Client Table
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class client extends Model
{
protected $primaryKey = 'clientID';
protected $fillable = ['fullNmae', 'firstName', 'lastName', 'phoneNumber', 'emailAddress'];
public function booking()
{
return $this->hasMany(Booking::class);
}
}
I tried adding the protected $primaryKey = 'bookingID'; and protected $primaryKey = 'clientID'; as suggested in my previous question but now I still can't get just the FirstName from the client table.
$bookingDetail = booking::with('client')->get();
return $bookingDetail->client->firstName;
You are trying to get a property from a collection in these lines:
$bookingDetail = booking::with('client')->get();
return $bookingDetail->client->firstName;
but this property client is defined for object, not a collection. so to solve it you must take one object from the collection like the following:
$bookingDetail = booking::with('client')->first();
return $bookingDetail->client->firstName;
i thing you need to try this one:
in your booking model:
// 'App\Client' these is example you need to put your client model path
public function client()
{
return $this->belongsTo('App\Client','clientID');
}
in your client model:
public function booking()
{
return $this->hasMany('App\Booking' , 'clientID');
}

Yii2 REST create with fields()

Let's say that I had the following API set up:
Controller:
<?php
namespace app\modules\v1\controllers;
use yii;
class ResourceController extends \yii\rest\ActiveController
{
public $modelClass = 'app\modules\v1\models\Resource';
}
Model:
use yii;
class Resource extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'ResourceTable';
}
public function fields()
{
return [
'id' => 'ResourceID',
'title' => 'ResourceTitle',
];
}
}
where my table only has the two columns, ResourceID and Title.
When I try a GET request on the API, it works fine and returns the list of resources (or single resource in the case of resource/{id}) with the aliased field names. But when I try to POST to create a resource, I want to use the aliased field names (e.g. title instead of ResourceTitle). The problem is that the default CreateAction supplied by Yii does $model->load(), which looks for the field names in the table. If I use the aliased names then it returns an error. If I use the table field names, it works fine.
So my question is, is there a way to expose resource attributes to the end user where the field names (using the fields() function) are the same for reading and creating? If possible, I'd like to avoid writing my own CreateAction.
It's necessary to add rules for new virtual properties, if you want to $model-load() save parameters to them
class OrganizationBranch extends BaseOrganization{
public function rules()
{
return array_replace_recursive(parent::rules(),
[
[['organizationId', 'cityId'], 'safe'],
]);
}
public function fields() {
return ['id',
'cityId' => 'city_id',
'organizationId' => 'organization_id',
'address',
'phoneNumbers' => 'phone_numbers',
'schedule',
'latitude',
'longitude',
];
}
public function extraFields() {
return ['branchType', 'city'];
}
public function getOrganizationId() {
return $this->organization_id;
}
public function setOrganizationId($val) {
$this->organization_id = $val;
}
public function getCityId() {
return $this->city_id;
}
public function setCityId($val) {
$this->city_id = $val;
}
}
You can create getters/setters for alias.
public function getTitle(){ return $this->ResourceTitle; }
public function setTitle($val){ $this->ResourceTitle = $val ; }

Creating queries in Paris ORM

I have a collection of Recipes and each one contains Categories. This are my models:
class Recipe extends \Model {
public static $_table = "recipe";
public function categories() {
return $this->has_many_through('Category');
}
}
class Category extends \Model {
public static $_table = "category";
public function categories() {
return $this->has_many_through('Recipe');
}
}
And the table to relate both:
class CategoryRecipe extends \Model {
public static $_table = "category_recipe";
}
Now I need to create a query to get all the Recipes that are under one/more categories. What is the way to achieve this? I want to avoid making things like this:
$results = $app['paris']->getModel('CategoryRecipe')
->where_in("category_id",$selected_categories)
->find_many();
foreach($results as $result) {
$recipe = $app['paris']->getModel('Recipe')
->where('id',$result->recipe_id)
->find_one();
var_dump($receta->name);
}
Create filters? functions inside the models? Is not possible to make it more elegant?
That is pretty much how I would do it, but you can optimise in one way. Add relation functions to your linking/many-to-many table. Then instead of doing that extra query in your foreach loop you simply do:
foreach($results as $result) {
$recipe = $result->recipe()->find_one();
var_dump($recipe)
}
So your CategoryRecipe model might look like:
class CategoryRecipe extends \Model {
public static $_table = "category_recipe";
public function recipe() {
$this->belongs_to('Recipe', 'recipe_id');
}
public function category() {
$this->belongs_to('Category', 'category_id');
}
}
I haven't tested this code, but it should be what you're after I think.

Please explain the foreign_key and local_key in Laravel ORM relationships

I'm effectively trying to define the relationships between users (sender and recipient) and messages.
My Messages migration is:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMessagesTable extends Migration {
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
//
Schema::create('messages', function($t){
$t->increments('id');
$t->integer('sender_user_id')->unsigned();
$t->integer('recipient_user_id')->unsigned();
$t->string('subject');
$t->text('content');
$t->timestamps();
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
//
Schema::dropIfExists('messages');
}
}
My Message model was straightforward:
<?php
class Message extends Eloquent{
// link to sender user id
public function from(){
return $this->hasOne('User', 'sender_user_id');
}
// link to recipient user id
public function to(){
return $this->hasOne('User', 'recipient_user_id');
}
}
But I'm unsure in defining the hasMany relationships in my User model.
The docs (http://laravel.com/docs/eloquent#relationships) shows the following:
return $this->hasMany('Comment', 'foreign_key');
return $this->hasMany('Comment', 'foreign_key', 'local_key');
Now, I'm confused which key is which in the latter hasMany relationship. Which is correct for my User model?
public function sentMessages(){
return $this->hasMany('Messages', 'id', 'sender_user_id');
}
public function sentMessages(){
return $this->hasMany('Messages', 'sender_user_id', 'id');
}
You have to set your relation like this:
public function sentMessages()
{
return $this->hasMany('Messages', 'sender_user_id');
}
public function receivedMessages()
{
return $this->hasMany('Messages', 'recipient_user_id');
}

How to extend cactiverecord

I want add aditional methods in CActiveRecord, but if class Project_Model extends CActiveRecord {} get error
The table "Project_ActiveRecord" for active record class "Project_ActiveRecord" cannot be found in the database.
I want create simple structure: CActiveRecord->Project_ActiveRecord (only extend methods)->Table (real table).
How can do this?
The error is clear: You don't have a table named Project_ActiveRecord in your DB!
If Project_Model is going to be the base model for your others active record models then you should do something like:
//A base classe example that has a behavior shared by all the inherited class
abstract class Project_Model extends CActiveRecord
{
public function behaviors(){
return array(
'CTimestampBehavior' => array(
'class' => 'zii.behaviors.CTimestampBehavior',
'setUpdateOnCreate' => true
),
);
}
}
And then you can declare the other class that will have a table in the db:
class YourClass extends Project_Model
{
/**
* Returns the static model of the specified AR class.
* #param string $className active record class name.
* #return Token the static model class
*/
public static function model($className=__CLASS__)
{
return parent::model($className);
}
/**
* #return string the associated database table name
*/
public function tableName()
{
return 'YourClassTable';
}
...
}
Then you shoudl never call the class Project_Model (this is why I put the keyword abstract) in your code, you have to call the inherited classes that have an existing table in the db!