How to specify Books and Authors relationship in Eloquent insertion? - php

I was figuring out how to insert a hasMany entry using Laravel 4
Author.php module
class Author extends Eloquent\LaravelBook\Ardent\Ardent
{
public static $rules = array(
'title' => 'required',
'last_name' => 'required',
'first_name' => 'required',
);
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'authors';
public function abstraction()
{
return $this->belongsTo('Book','book_id');
}
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getRegId()
{
return $this->getKey();
}
}
Book.php Module
class Book extends Eloquent\LaravelBook\Ardent\Ardent
{
public static $rules = array(
'title' => 'required',
'authors' => 'required',
);
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'abstracts';
public function author()
{
return $this->hasMany('Author');
}
/**
* Get the unique identifier for the user.
*
* #return mixed
*/
public function getRegId()
{
return $this->getKey();
}
}
My controller
$r = new Book();
$r->title = Input::get('title');
$r->authors = Input::get('authors');
$r->affiliation = Input::get('affiliation');
$r->keywords = Input::get('keywords');
$r->summary = Input::get('summary');
$r->save();
$authors = new Author();
$authors_array = array(
array('name' => 'Arun1', 'affiliation' => 'aff_arun'),
array('name' => 'Arun2', 'affiliation' => 'aff_arun'),
array('name' => 'Arun3', 'affiliation' => 'aff_arun3'),
array('name' => 'Arun4', 'affiliation' => 'aff_arun'),
);
$authors->book()->associate($r);
$authors->save($authors_array);
I'm getting one null record on authors table which pointed to the book and other 3 record that I inserted here without any pointing to the book.

First off I believe this is wrong schema, since probably one author could write more than a single book.
So I suggest you define many-to-many (belongsToMany) relation for this instead.
Anyway here is solution to your problem:
// I wonder why you call the book $r ?
$r = new Book;
...
$r->save();
$authors_array = array(...);
foreach ($authors_array as $author)
{
$r->author()->save(new Author($author));
// you need $fillable/$guarded on Author model or it will throw MassAssignementException
}
Another suggestion: rename the relation to authors so it is meaningful and you won't catch yourself trying to treat it like a single model (while it always returns the Collection)

Related

Return many object in my json response using resource

I'm kinda new to Laravel and I hope someone we'll be able to give me some help.
I apologize for my english
So I'm trying to develop an application with some friends to manage our food by sending alert when the peremption date is near.
I'm developing the API, the actual structure is this way:
A user,
A product,
A basket containing the user_id, the product_id and of course the peremption date.
So now when I make a call to get the User 'stock' on my API I wish I could get something like this:
{
'id' : 1,
'peremption_date': XX-XX-XX,
'product' : {
'id' : 3,
'name': bblablabala,
'brand' : blablabala
},
'id' : 2,
'peremption_date': XX-XX-XX,
'product' : {
'id' : 4,
'name': bblablabala,
'brand' : blablabala
},
}
So I took a look on resources and saw that if I define the right relations, this could do the stuff for my output.
I'll link you my actual class declarations and their resources:
User:
//user.php
class User extends Authenticatable
{
use Notifiable, HasApiTokens;
protected $fillable = [
'name', 'email', 'password',
];
protected $hidden = [
'password', 'remember_token',
];
public function baskets()
{
return $this->hasMany(Basket::class);
}
}
Product:
//Product.php
class Product extends Model
{
protected $table = 'products';
protected $fillable = ['code_barre', 'product_name', 'generic_name', 'brand', 'quantity'];
public function basket()
{
return $this->belongsToMany(Basket::class);
}
}
//productResource.php
class ProductResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'code_barre' => $this->code_barre,
'product_name' => $this->product_name,
'generic_name' => $this->generic_name,
'brand' => $this->brand,
'quantity' => $this->quantity,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
];
}
}
Basket:
//Basket.php
class Basket extends Model
{
protected $table = 'baskets';
protected $fillable = ['user_id', 'product_id', 'dlc_date'];
public function user()
{
return $this->belongsTo(User::class);
}
public function product()
{
return $this->hasOne(Product::class);
}
}
//BasketResource.php
class BasketResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'dlc_date' => (string) $this->dlc_date,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at,
'product' => $this->product
];
}
}
So when I try to store a new basket in my store method:
//BasketController.php
public function store(Request $request)
{
$this->product->storeProduct($request->input('code_barre'));
$att = DB::table('products')
->where('code_barre', '=', $request->input('code_barre'))
->first();
$basket = Basket::create([
'user_id' => $request->user()->id,
'product_id' => $att->id,
'dlc_date' => $request->input('dlc_date')
]);
return new BasketResource($basket);
}
I get the following error (this one)
saying than products.id_basket does not exist and its right, it's not supposed to exist. This is Basket who have a product_id. so I know this is coming from the relationship I declared but I can't figure how to do it right.
Can someone come and save me ???
Thanks a lot, I hope you understood me !
Wish you a good day
As I look at your Basket model, it seems you have to change your:
public function product()
{
return $this->hasOne(Product::class);
}
to:
public function product()
{
return $this->belongsTo(Product::class);
}
Because you have product_id in your baskets table. To use hasOne() relation, you will need to remove product_id from baskets table and add basket_id to products table, because hasOne() relation is something like hasMany(), only calling ->first() instead of ->get()

Phalcon php orm can't relate tables

I'm trying to setup a 3 tables relationship in phalcon, but it doesn't seem to work. Could anyone plese tell me what am I doing wrong? Here is a ERD of these tables: (ERD diagram) Here is my code from models and controller
Authors.php (Model)
<?php
class Authors extends \Phalcon\Mvc\Model
{
/**
*
* #var integer
*/
public $ID;
/**
*
* #var string
*/
public $Name;
/**
*
* #var string
*/
public $LastName;
/**
* Initialize method for model.
*/
public function initialize()
{
$this->setSource('Authors');
$this->hasMany('ID', 'Authorbook', 'AuthorID');
}
/**
* Independent Column Mapping.
*/
public function columnMap()
{
return array(
'ID' => 'ID',
'Name' => 'Name',
'LastName' => 'LastName'
);
}
Books.php(Model)
<?php
class Books extends \Phalcon\Mvc\Model
{
/**
*
* #var integer
*/
public $ID;
/**
*
* #var string
*/
public $Name;
/**
*
* #var string
*/
public $YearPublished;
/**
*
* #var string
*/
public $Picture;
/**
* Initialize method for model.
*/
public function initialize()
{
$this->setSource('Books');
$this->hasMany('ID', 'Authorbook', 'BookID');
}
/**
* Independent Column Mapping.
*/
public function columnMap()
{
return array(
'ID' => 'ID',
'Name' => 'Name',
'YearPublished' => 'YearPublished',
'Picture' => 'Picture'
);
}
}
}
AuthorBook.php (Model):
<?php
class Authorbook extends \Phalcon\Mvc\Model
{
/**
*
* #var integer
*/
public $ID;
/**
*
* #var integer
*/
public $AuthorID;
/**
*
* #var integer
*/
public $BookID;
/**
* Initialize method for model.
*/
public function initialize()
{
$this->setSource('AuthorBook');
$this->belongsTo('AuthorID', 'Authors', 'ID');
$this->belongsTo('BookID', 'Books', 'ID');
}
/**
* Independent Column Mapping.
*/
public function columnMap()
{
return array(
'ID' => 'ID',
'AuthorID' => 'AuthorID',
'BookID' => 'BookID'
);
}
}
AdminController.php
public function indexAction()
{
$this->view->disableLevel(View::LEVEL_MAIN_LAYOUT);
$this->view->setVar('books', Books::find()->toArray());
}
}
So my question is how can I get access to the author of a book? Because when I print out my books array in view only thing I get is :
Array
(
[0] => Array
(
[ID] => 1
[Name] => Javascript: The Good Parts
[YearPublished] => 2014-04-18
[Picture] => javascript-the-good-parts.jpg
)
...
You should use the hasManyToMany for n-n relationship for the Authors and Books models, (if I'm not wrong, Author has many Books and Books has many Authors)
so you models should be like this:
for Authors:
public function initialize(){
$this->setSource('Authors');
$this->hasManyToMany('ID', 'Authorbook', 'AuthorID', 'BookID', 'Books', 'ID', array(
'alias' => 'books'
));
}
same for the Books model, we have to use the hasManyToMany function:
public function initialize(){
$this->setSource('Books');
$this->hasManyToMany('ID', 'Authorbook', 'BookID', 'AuthorID', 'Author', 'ID', array(
'alias' => 'authors'
));
}
the AuthorBook model relations are correct.
Now you can get the author(s) of a book with a simple call to the alias that we have defined in the relations:
$book = Books::findFirst();
$bookAuthor = $book->authors->toArray()
that's it.
I copied an example code on Phalcon site to test model relationship and ran into similar issue. I need to specific the namespace when set relationship. For example, in function initialize of Robots class
public function initialize()
{
$this->getSource('robots');
$this->hasMany('id', 'SomeNameSpace\RobotsParts', 'robots_id',array(
'alias' => 'robotsparts',
));
}

Laravel Confide not triggering eloquent.saving

I'm using zizaco/confide in combination with cviebrock/eloquent-sluggable.
eloquent-sluggable uses Events::listen('eloquent.saving*') for generating the slug while/before saving.
// Cviebrock\EloquentSluggable\SluggableServiceProvider:55
public function registerEvents()
{
$app = $this->app;
$app['events']->listen('eloquent.saving*', function($model) use ($app)
{
$app['sluggable']->make($model);
});
}
Since I switched to Confide for authentication the slugs are not getting generated.
My user model is simply class User extends ConfideUser. Switching to class User extends Ardent or User extends Eloquent the event eloquent.saving is getting triggered fine.
I'm not shure if this is a bug or I'm missing something.
My Model:
<?php
use Illuminate\Database\Eloquent\Model;
use Zizaco\Confide\ConfideUser;
class User extends ConfideUser
{
public $autoPurgeRedundantAttributes = true;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* Soft delete
*
* #var boolean
*/
protected $softDelete = true;
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = array(
'nickname',
'password',
'email',
'deleted_at',
'disabled',
'firstname',
'lastname',
'birthday',
// needed by ardent
'email_confirmation',
'password_confirmation'
);
public static $rules = array(
'firstname' => 'required',
'email' => 'required|email|confirmed|unique:users',
'email_confirmation' => 'required',
'nickname' => 'required|min:2|unique:users',
'birthday' => 'date:d.m.Y|before:now',
'password' => 'required|min:5|confirmed',
'password_confirmation' => 'required'
);
public $imageSizes = array(
array(64, 64),
array(250, 250)
);
public static $sluggable = array(
'build_from' => 'nickname',
'save_to' => 'slug',
);
/**
* Roles
*
* #return object
*/
public function roles()
{
return $this->belongsToMany(
'Role',
'role_user'
)
->withTimestamps();;
}
}
Seems to me like this one is a bug: https://github.com/Zizaco/confide/issues/179
As a temporary workaround you can wrap the beforeSave() method in your Model without returning anything (!):
public function beforeSave($forced = false)
{
parent::beforeSave($forced);
}

Css is not loading after changed the GetDbConnection() in Yii

I'm new to Yii Framwork, I have used the steps from Multiple-database support in Yii to connect different database , its helped me lot.
but Css is not loaded, normal HTML content is displaying in browser when I'm opening the index.php
What changes is require to load the CSS after changing the GetDbConnection() in modules.
my Ad.php code from models
<?php
class Ad extends MyActiveRecord
{
public $password;
public $repassword;
public function getDbConnection()
{
return self::getCCDbConnection();
}
public static function model($className=__CLASS__)
{
return parent::model($className);
}
....
}
Thanks in Advance
This does not solve your css problem, however, this is the right way to use multiple dbs in yii.
This is the right way to use multiple db's in yii mvc:
Let's say that i have multiple db's and I use them to store urls.
From time to time I need to change the db.
So, I have the model generated by using gii and on top of that I have class that extends and overwrites some of methods/functions.
UrlSlaveM extends UrlSlave wich extends CActiveRecord
as default, in UrlSlave I will connect to my first db
I always use UrlSlaveM when I insert new data, so that I can overwrite the following function:
public function getDbConnection() {
return Yii::app()->db1;
}
here is a full SlaveUrl model:
<?php
/**
* This is the model class for table "url".
*
* The followings are the available columns in table 'url':
* #property string $id
* #property integer $instance_id
* #property integer $website_id
* #property string $link
* #property string $title
* #property integer $created
* #property integer $updated
* #property integer $status
*/
class UrlSlave extends CActiveRecord {
/**
* Returns the static model of the specified AR class.
* #param string $className active record class name.
* #return UrlSlave the static model class
*/
public static function model($className = __CLASS__) {
return parent::model($className);
}
/**
* #return CDbConnection database connection
*/
public function getDbConnection() {
return Yii::app()->db1;
}
/**
* #return string the associated database table name
*/
public function tableName() {
return 'url';
}
/**
* #return array validation rules for model attributes.
*/
public function rules() {
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('instance_id, website_id, link, title, created, updated, status', 'required'),
array('instance_id, website_id, created, updated, status', 'numerical', 'integerOnly' => true),
array('link, title', 'length', 'max' => 255),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, instance_id, website_id, link, title, created, updated, status', 'safe', 'on' => 'search'),
);
}
/**
* #return array relational rules.
*/
public function relations() {
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
);
}
/**
* #return array customized attribute labels (name=>label)
*/
public function attributeLabels() {
return array(
'id' => 'ID',
'instance_id' => 'Instance',
'website_id' => 'Website',
'link' => 'Link',
'title' => 'Title',
'created' => 'Created',
'updated' => 'Updated',
'status' => 'Status',
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
* #return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
*/
public function search() {
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria = new CDbCriteria;
$criteria->compare('id', $this->id, true);
$criteria->compare('instance_id', $this->instance_id);
$criteria->compare('website_id', $this->website_id);
$criteria->compare('link', $this->link, true);
$criteria->compare('title', $this->title, true);
$criteria->compare('created', $this->created);
$criteria->compare('updated', $this->updated);
$criteria->compare('status', $this->status);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
));
}
}
and here is the full UrlSlaveM model:
<?php
class UrlSlaveM extends UrlSlave {
const ACTIVE = 1;
const INACTIVE = 0;
const BANNED = -1;
public static function model($className = __CLASS__) {
return parent::model($className);
}
public function rules() {
$parent_rules = parent::rules();
$rules = array_merge(
$parent_rules, array(
array('link', 'unique'),
));
return $rules;
}
public static $server_id = 1;
public static $master_db;
public function getDbConnection() {
//echo __FUNCTION__;
//die;
//echo 111;
self::$master_db = Yii::app()->{"db" . self::$server_id};
if (self::$master_db instanceof CDbConnection) {
self::$master_db->setActive(true);
return self::$master_db;
}
else
throw new CDbException(Yii::t('yii', 'Active Record requires a "db" CDbConnection application component.'));
}
}
now, by setting $server_id to 1 or 2 or 3 ... you are able to connect to another db
please set the value of $server_id as UrlSlaveM::$server_id = 2; before you add data!
public static $server_id = 1;
public static $master_db;
also, in the main config file, set like this:
'db' => array(
'connectionString' => 'mysql:host=localhost;dbname=dvc',
'emulatePrepare' => true,
'username' => 'root',
'password' => '',
'charset' => 'utf8',
),
'db2' => array(
'connectionString' => 'mysql:host=localhost;dbname=dvc2',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'tablePrefix' => '',
'class' => 'CDbConnection' // DO NOT FORGET THIS!
),
'db1' => array(
'connectionString' => 'mysql:host=localhost;dbname=dvc1',
'username' => 'root',
'password' => '',
'charset' => 'utf8',
'tablePrefix' => '',
'class' => 'CDbConnection' // DO NOT FORGET THIS!
),

Yii Model extending / overloading

I have an Account Model which has some properties and relations, and i have an Reseller model.
The point is that a reseller is actually an account only it has the possibility to have accounts beneath it.
What is the best approach to implement this,
at first i had a special Reseller class with relations between them, but actually i just want an accounts class which if the account is a reseller uses the reseller class.
Account model
<?php
/**
* This is the model class for table "account".
*
* The followings are the available columns in table 'account':
* #property string $id
* #property string $reseller_id
* #property string $name
* #property string $invoice_id
* #property boolean $is_reseller
*
* The followings are the available model relations:
* #property Reseller $reseller
* #property Contact[] $contacts
* #property Domain[] $domains
* #property HostingAccount[] $hostingAccounts
* #property User[] $users
*/
class Account extends CActiveRecord {
/**
* Returns the static model of the specified AR class.
* #param string $className active record class name.
* #return Account 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 'account';
}
/**
* #return array validation rules for model attributes.
*/
public function rules() {
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('name', 'required'),
array('id, reseller_id', 'length', 'max' => 40),
array('name', 'length', 'max' => 45),
array('invoice_id', 'length', 'max' => 10),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, reseller_id, name, invoice_id', 'safe', 'on' => 'search'),
);
}
/**
* #return array relational rules.
*/
public function relations() {
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'reseller' => array(self::BELONGS_TO, 'Reseller', 'reseller_id'),
'contacts' => array(self::HAS_MANY, 'Contact', 'account_id'),
'domains' => array(self::HAS_MANY, 'Domain', 'account_id'),
'hostingAccounts' => array(self::HAS_MANY, 'HostingAccount', 'account_id'),
'users' => array(self::HAS_MANY, 'User', 'account_id'),
);
}
/**
* #return array customized attribute labels (name=>label)
*/
public function attributeLabels() {
return array(
'id' => 'ID',
'reseller_id' => 'Reseller',
'name' => 'Name',
'invoice_id' => 'Invoice',
);
}
/**
* Retrieves a list of models based on the current search/filter conditions.
* #return CActiveDataProvider the data provider that can return the models based on the search/filter conditions.
*/
public function search() {
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria = new CDbCriteria;
$criteria->compare('id', $this->id, true);
$criteria->compare('reseller_id', $this->reseller_id, true);
$criteria->compare('name', $this->name, true);
$criteria->compare('invoice_id', $this->invoice_id, true);
return new CActiveDataProvider($this, array(
'criteria' => $criteria,
));
}
/**
* Adds UUID before the item is saved
*
*/
public function beforeSave() {
if ($this->isNewRecord)
$this->id = new CDbExpression('UUID()');
return parent::beforeSave();
}
}
Reseller Model
<?php
class Reseller extends Account
{
/**
* #return array relational rules.
*/
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'reseller' => array(self::BELONGS_TO, 'Reseller', 'reseller_id'),
'contacts' => array(self::HAS_MANY, 'Contact', 'account_id'),
'domains' => array(self::HAS_MANY, 'Domain', 'account_id'),
'hostingAccounts' => array(self::HAS_MANY, 'HostingAccount', 'account_id'),
'users' => array(self::HAS_MANY, 'User', 'account_id'),
'accounts' => array(self::HAS_MANY, 'Account', 'reseller_id'),
'account' => array(self::BELONGS_TO, 'Account', 'account_id'),
);
}
}
First keep in mind that ActiveRecord != models its easy to get confused, beware!
Also check this post
Ok now, you can have some factory method that gives you the class you need, Account or Reseller, depending on your logic, if not all acounts can be resellers you may also need some way to determine this. like a "is_reseller" column or similar.
In the end i made a relation from accounts to itself and solved it.
/**
* #return array relational rules.
*/
public function relations() {
return array(
'reseller' => array(self::BELONGS_TO, 'Account', 'account_id'),
'users' => array(self::HAS_MANY, 'User', 'account_id'),
'accounts' => array(self::HAS_MANY, 'Account', 'account_id'),
);
}
I used a extended classes to a models to implemented a diferents methos and a unique prerequisite is add this function in a new class:
public static function model($className=__CLASS__){
return parent::model($className);
}

Categories