Laravel insert relationship in newly created model - php

I have a relationship set up between two models. The two models are set up like:
app/Character.php
public function characteristics() {
return $this->hasMany('App\Characteristics');
}
app/Characteristics.php
public function character() {
return $this->belongsTo('App\Character');
}
then in a controller I have a method to create a new character with a predetermined set of characteristics as follows:
app/Http/Controllers/CharacterController.php
public function newCharacter(Request $request) {
$character = new Character();
$characteristics = getCharacteristics($character->id);
// Save basic character stuff
$character->characteristics()->saveMany($characteristics);
$character->save();
}
The highlighted line is throwing an error because saveMany is not a function of Builder so how can I get the created character without having to do a find that would have to hit the database again?

I think you need to save the character model first, because if you're building a hasMany/belongsTo relationship your characteristics table must have a column for character_id, and when you do $character->characteristics()->saveMany($characteristics); Laravel will try to insert the ID from the parent model. And as per the code shared by you, you've just instantiated the model, by this point of time it doesn't have an ID associated with it. So you need to save the character model first and assuming the getCharacteristics() method is returning an array/collection of Characteristics Models, Following should work:
public function newCharacter(Request $request) {
$character = new Character();
$character->save();
$characteristics = getCharacteristics();
// Save basic character stuff
$character->characteristics()->saveMany($characteristics);
}
And to further clarify this for you, from the characteristics method in your Character model an instance of HasMany not a Builder is being returned, thus saveMany works here.

Related

Failure to extract data from a relationship with laravel

I am trying to make a one-to-many relationship, but I get the following error
Undefined property: stdClass::$client (View:
C:\wamp\www\intranet\resources\views\users\list.blade.php)
The problem is that I am working with an existing database that in the tables does not have id fields, and the foreign keys would also be the typical ones like client_id
My model Client.php
class Client extends Model
{
protected $connection = 'dpnmwin';
protected $table = 'nmundfunc';
public function employee(){
return $this->hasMany('App\Employee');
}
}
My model Employee.php
class Employee extends Model
{
protected $connection = 'dpnmwin';
protected $table = 'nmtrabajador';
public function client(){
return $this->belongsTo('App\Client', 'COD_UND');
}
}
In nmtrabajador COD_UND field would be the foreign key that relates to nmundfunc.
And I try to get the data out like this: {{$user->client->CEN_DESCRI}}.
but it does not throw me the error, how can I solve it?
My Controller where I send in sight
public function index(){
$users = DB::connection('dpnmwin')->table('nmtrabajador')->where('CONDICION', '=', 'A')->get();
return view('users.list',array(
'users' => $users
));
}
You have to call basis on relations.
This code will return you data.
If you have id then you can find by id like below
$employee=Employee::find(1);
Or if you want to fetch all data then you can call all method.
Employee::all();
And then you can just get it by relation as you define in models.
$client=$employee->client->CEN_DESCRI;
Retrieving data from the instance is based on the methods which we have use.
Here in this answer, you can get that
Property [title] does not exist on this collection instance
I hope it will work.
If table doesn't have 'id' as primary key you should specify what the primary key is inside your model:
protected $primaryKey = 'your_primary_key';
Relation looks good, after that you must make sure $user is a defined instance of Employee, because your error probably means that your instance wasn't even defined, so for example if you are using list.blade.php, you need to change the return of your controller and indicate that you want to pass data to your view, for example you could do it like this:
return view('users.list', compact('user'));
Where user is an instance of Employee saved on '$user'
Update
First you should check your user is retrieved properly, you can check it by placing a dd($user)
And when you return a view you can pass information to it, a cleaner way of doing what you are trying to do is what I wrote earlier so you would end up having something like this:
public function index()
{
$users = DB::table('nmtrabajador')
->where('CONDICION', '=', 'A')
->get();
// dd($user) for debugging you are retrieving the user properly
return view('users.list', compact($users));
}

Custom method chaining with Eloquent ORM?

This is my current query:
$cars = Cars::with('brand')->get();
$cars->map(function($cars){
$cars->fullName = $cars->brand->brand." ".$cars->name;
//other manipulation...
return $cars;
});
I want to manipulate my collection in the model so that I can run something like $cars = Cars::with('brand')->getWithBrand();
How can I do this, so I don't have to write map functions for every time I run the query?
In your particular example, you don't need to use map to modify the Collection at all. You can use an Eloquent accessor to define attributes on a Model that don't exist in the database. In your example, you would define the following method on your Cars model:
public function getFullNameAttribute($value)
{
// make sure brand exists first
if ($this->brand) {
return $this->brand->brand.' '.$this->name;
}
// default if brand doesn't exist
return $this->name;
}
By defining that function on your Model, that function will be called whenever you attempt to use the full_name attribute, as shown in the following code:
$car = Cars::with('brand')->first();
// this will echo the result of the getFullNameAttribute method
echo $car->full_name;
Edit
If you would also like this new attribute to automatically show up in your toArray() or toJson() output, you can add the attribute to the $appends property on your Cars model:
class Cars extends Model
{
protected $appends = ['full_name'];
public function getFullNameAttribute($value)
{
// make sure brand exists first
if ($this->brand) {
return $this->brand->brand.' '.$this->name;
}
// default if brand doesn't exist
return $this->name;
}
}
Be aware, however, that your custom attribute depends on a related object. So, if you do something that accidentally calls toArray(), toJson(), __toString(), etc on a Collection of Cars that has not eager loaded the brand relationship, this will cause the N+1 query issue.
For example:
// Bad: N+1 issue because each printed Car will execute a
// separate query to get its brand to output full_name.
echo Cars::get();
// Good: No N+1 issue because all brands are already loaded.
echo Cars::with('brand')->get();

Scope And Relationship on Same Model

I have a table called payments which contains a field called Vendor ZIP.
I have a table called 201502_postcodes and my "join" in this case is the postcode field in this table.
How do I return field values in this 201502_postcodes table using Eloquent?
My Models are;
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Payment extends Model {
public function postcodeExtract()
{
return $this->belongsTo('App\Models\PostcodeExtract', 'postcode', 'Vendor ZIP');
}
_
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PostcodeExtract extends Model {
protected $connection = 'postcodes';
public function scopeFromTable($query, $tableName)
{
return $query->from($tableName);
}
public function payment()
{
return $this->hasMany('App\Models\Payment', 'Vendor ZIP', 'postcode');
}
So, I have a scope on this model because the 201502 part of my table name is a variable (in that, a new one comes in every quarter).
In my controller... I have no idea what to put. I don't know how to get both scope and relationship to work. How can I write a query that will take a postcode/zip and output one of the fields from the (do I refer to them as "methods"?) postcode extract table?
It is not a duplicate of this question Laravel 4: Dynamic table names using setTable() because relationships are not involved or discussed on that question.
--- UPDATE ---
If I am to use getTable - would it go something like this...
class PostcodeExtract {
public function setTableByDate($selected_tablename)
{
$this->table = $selected_tablename;
// Return $this for method chaining
return $this;
}
public function getTable()
{
if (isset($this->table))
$this->setTableByDate($this->table);
return $this->table;
}
}
And then I would use it in my controller like;
$selected_tablename = 201502_postcode //created by some other controller
$postcode_extract = new PostcodeExtract;
$data = $postcode_extract->setTableByDate($selected_tablename)->get()->toArray();
The Carbon stuff isn't really relevant. I have a lookup to get those tablenames the fact the prefix with a date like value shouldn't mean it's treated like a date.
There are a couple of things going on here.
scopeFromTable() is redundant
Laravel employs magic methods to handle calls to undefined methods. Calling from() on the model will actually call from() on the models internal Query object (assuming you didn't define a method called 'from' on the model itself). It's worth reading the __call and __callStatic methods on the Model class.
relationships use getTable()
Another aspect of the Laravel is the concept of convention over configuration. This basically means that the framework assumes some things so that you don't have to define every detail. In regards to table naming convention, it will naturally use a table name derived from the class name.
// Uses table 'foos'
class Foo {}
There are a few ways to change this behavior. First, you can define a 'table' data member like this.
class Foo {
protected $table = 'bars';
}
If you need a more dynamic behavior, then you can redefine the getTable method.
class Foo {
public function getTable()
{
// return your special table name based on today's date
}
}
Ultimately the models and their relationships refer to getTable to figure out what the table names should be.
your use cases
If you only ever need to query the current table, then I would suggest redefining getTable.
If you need to query both current and past tables, then I suggest pairing a new method along side redefining getTable
class Foo {
public function setTableByDate(\DateTime $date)
{
$this->table = // generate table name from $date
// Return $this for method chaining
return $this;
}
public function getTable()
{
if (isset($this->table))
$this->setTableByDate(\Carbon\Carbon::now());
return $this->table;
}
}
With this in place, you don't have to worry about the table name in your controller or anywhere else unless you need to query past records.
setting the table by date per user
$foos = Foo::setTableByDate($user->some_date)->where(...)->get();

Save multiple records in multiple tables after save model in Yii

I have the followings tables:
tbl_project(id, description)
tbl_operation(id, project_id, name)
tbl_itemType(id, operation_id, name)
tbl_item(id, itemType_id, name, unit, price)
I wanna when i create a new project, it adds some operations in tbl_operation and then adds some itemTypes to tbl_itemType and then adds some items in tbl_item. How can i do it in afterSave() behavior of project's model?
I read the following link, but i don't know is it possible to do by this?
esaverelatedbehavior
just create a function in your ProjectModel
public function afterSave()
{
$operation_model = new Operation();
$operation_model->setAttributes($YOUR_DATA);
$operation_model->save(); // create your operation
// same goes for every other data you want to save
return parent::afterSave(); // keep the chain
}
You could make use of the relations. This approach will only work if the respective relation contains only the models to be saved. In your controller have
$project->operations = array(/*your operations*/);
In turn each operation model could also have the related itemTypes
$operation->itemTypes = array(/*itemTypes for this operation*/)
And lastly each itemType could have the related items.
And in your afterSave for operations have
public function afterSave() {
foreach ($this->operation as $op) {
$op->project_id = $model->id;
$op->save();
}
return parent::afterSave();
}
For the afterSave for the Operation and ItemType classes should in turn save the related ItemTypes and Items respectively.
Better use afterSave() function , i think it will work for you

Custom logic for updating a row

I have this table class:
class Songs extends Zend_Db_Table_Abstract
{
protected $_name = 'songs';
protected $_primary = 'song_id';
protected $_rowClass = 'Song';
}
And a class that extends the class above with some custom logic.
class Song extends Zend_Db_Table_Row_Abstract
{
protected function _insert()
{
print_r($this);
// $this does exist
}
protected function _update()
{
print_r($this);
//$this does not existing when updating a row, why not?
}
}
My problem is that when I'm inserting a new row I can use $this in my custom logic.
$row->save(); // $this exists in _insert()
But it doesn't exist when I'm trying to update a row.
$myRow->update($data, $where); // $this does not exists in _update()
Why does $this not exist when I want to do some custom logic before updating a row?
To update a row, you don't use:
$myRow->update($data, $where);
You use:
$myRow->save();
But trying to use update() on a row object should throw an exception.
So I'm guessing you're actually calling the update() function on the table object, and not the row object.
$songs = new Songs();
//...
$songs->update($data, $where);
At that point the row object is never even used, the query is simply generated from the $data array and the $where clause.
If you want to use the custom _update() method you would need to do something like:
$songs = new Songs();
$song = $songs->find($id)
//change some data
$song->save();
Of course is also perfectly valid to add custom logic at the table level, and should be noted while calling an update or insert from the table object does not use the row object, calling save() on the row object proxies the table object.
For example, from the Zend_Db_Table_Row _doInsert() function:
$this->_insert();
//...
$primaryKey = $this->_getTable()->insert($data);
So if you have custom logic that you want to use every time you update a row (whether you update from the table object or the row object), it should be put into the table object.
From the Zend_Db_Table_Row docs:
If you need to do custom logic in a specific table, and the custom logic must occur for every operation on that table, it may make more sense to implement your custom code in the insert(), update() and delete() methods of your Table class. However, sometimes it may be necessary to do custom logic in the Row class.

Categories