Make Trait protected attributes accessible to Eloquent in Laravel 4.2 - php

I'm new to using Traits and having trouble saving the protected attributes of my trait within my eloquent model:
Here is my Route model:
namespace App\Models;
use Eloquent;
class Route extends Eloquent {
use CardTrait {
CardTrait::__construct as __CardConstruct;
}
public $timestamps = false;
protected $table = 'routes';
protected $primaryKey = 'id';
protected $visible = [
'name',
'description'
];
public function __construct(array $attributes = array())
{
$this->__CardConstruct($attributes);
}
//relationships follow
}
and here is the CardTrait trait:
namespace App\Models;
trait CardTrait {
protected $timesAsked;
protected $factor;
protected $nextTime;
public function __construct($attributes = array(), $timesAsked = 0, $factor = 2.5, $nextTime = null)
{
parent::__construct($attributes);
if (is_null($nextTime)) $nextTime = \Carbon::now()->toDateTimeString();
$this->factor = $factor;
$this->nextTime = $nextTime;
$this->timesAsked = $timesAsked;
public function answer($difficulty)
{
if($difficulty < 3)
$this->timesAsked = 1;
}
//other methods follow
}
In my controller I can use:
$route = new Route();
$route->name = "New Name";
$route->description = "New Description";
$route->answer(5);
$route->save();
name and description save fine, and although I have columns for timesAsked, factor and nextTime When I dd($route) I can see
protected 'timesAsked' => int 1
protected 'factor' => float 2.6
protected 'nextTime' => string '2015-04-15 21:36:53' (length=19)
so I know the methods of the Trait are working fine.
My question is how can I save these values with Eloquent so that these can be stored and retrieved from the database?
Thanks in advance.

Eloquent will store values in an internal attributes array. This is what is actually written to the database. It will not write values to the attributes array if they already exist as a data member on the model. Look into the __set and __get magic methods and how they are used in Illuminate/Database/Eloquent/Model
So long story short, remove the protected data members from the model.

Related

Eloquent: guard ID when inserting into one database but don't guard it for another database.

I need to guard the ID column when inserting into a database, however I don't want to guard it when inserting into a different database due to needing to manually set the ID, so that the tables are in sync.
However I can't figure out a way to do it, below is what I have got at the moment, however this doesn't work at all as I just get an error:
Field 'id' doesn't have a default value
This is my current model:
<?php
namespace App\Models\Seasonal;
use Illuminate\Database\Eloquent\Model;
class SeasonalBanner extends Model
{
protected $connection = 'dev';
protected $guarded = [ 'id' ];
protected $appends = [ 'period' ];
public static function boot()
{
parent::boot();
self::creating(function($model){
if ($model->connection === 'live') {
$model->guarded = [];
}
});
}
public function dates() {
return $this->hasMany(SeasonalBannerDates::class);
}
public function getPeriodAttribute() {
return [ $this->start, $this->end ];
}
}
The best way in my opinion is not to use $guarded at all in such case. Just set:
protected $guarded = [];
and in your code depending on which database you use, either fill id or not.

Laravel query with multiple where not returning expected result

I'm trying to build a query from a Repository in a Model with 2 where clauses.
This is the data I have in a MySql table:
id name environment_hash
1 online_debit abc
2 credit_cart abc
I want to query by name and environment_hash. To do this, I created the method findByHashAndMethod() (see below).
But when I use it in my controller, like this:
$online_debit = $this->ecommercePaymentMethodRepository->findByHashAndMethod($hash, 'online_debit')->first();
or this:
$credit_card = $this->ecommercePaymentMethodRepository->findByHashAndMethod($hash, 'credit_cart')->first();
I keep getting both rows and not only the ones filtered. What's wrong with the code?
This is my PaymentMethodRepository.php
class EcommercePaymentMethodRepository extends BaseRepository
{
public function findByHashAndMethod($hash = null, $payment_method)
{
$model = $this->model;
if($hash)
{
$filters = ['environment_hash' => $hash, 'name' => $payment_method];
$this->model->where($filters);
}
else
{
$this->model->where('environment_hash', Auth::user()->environment_hash)
->where('name', $payment_method);
}
return $model;
}
public function model()
{
return EcommercePaymentMethod::class;
}
}
And this is my model EcommercePaymentMethod.php
<?php
namespace App\Models;
use Eloquent as Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class EcommercePaymentMethod extends Model
{
use SoftDeletes;
public $table = "ecommerce_payment_methods";
protected $dates = ['deleted_at'];
public $fillable = [
"name",
"payment_processor_id",
"active",
"environment_hash"
];
protected $casts = [
"name" => "string"
];
public function payment_processor()
{
return $this->hasOne('App\Models\EcommercePaymentProcessor');
}
}
While I am not entirely sure why ->first() would ever return more than one result, your Repository method had some few glaring issues that's prone to errors.
class EcommercePaymentMethodRepository extends BaseRepository
{
// 1. Do not put optional parameter BEFORE non-optional
public function findByHashAndMethod($payment_method, $hash = null)
{
// 2. Call ->model() method
$model = new $this->model();
// 3. Logic cleanup
if (is_null($hash)) {
$hash = Auth::user()->environment_hash;
}
return $model->where('environment_hash', $hash)
->where('name', $payment_method);
}
public function model()
{
return EcommercePaymentMethod::class;
}
}

laravel mutator that creates a unique random number does not run

I have a Question model like this:
class Question extends Model
{
use SoftDeletes;
protected $primaryKey = 'question_id';
protected $dates = ['deleted_at'];
protected $fillable = ['text', 'code', 'cat', 'answer', 'is_private', 'email', 'parent'];
public function setCodeAttribute ($value)
{
\Debugbar::info('hello');
do {
$code = rand(10000, 10000000);
$user_code = User::where('code', $code)->get();
} while (!$user_code->isEmpty());
return $this->attributes['code'] = $code;
}
}
I want to generate a random integer number and store that in code field on creation of a new instance of the model.
For that I wrote a mutator as you can see but it did not even run.
What is problem?
Well mutator itself do not run untill you don't call $question->code = $someValue.
What you need is observer and define saving method in it that will provide setting of the code attribute value in it like this:
public function saving (Operation $operation)
{
\Debugbar::info('hello');
do {
$code = rand(10000, 10000000);
$user_code = User::where('code', $code)->get();
} while (!$user_code->isEmpty());
return $operation->code = $code;
}
and of corse you need to delete the mutator from the model class as well.

Laravel 4 - foreign key constraint fails

I have the following relations:
Discount:
<?php
class Discount extends Eloquent {
protected $table = 'discount';
public $timestamps = true;
public function title()
{
return $this->hasOne('Translation', 'labelId', 'titleLabelId')->where('languageId', T::getLang())->first()['phrase'];
}
public function titles()
{
return $this->hasMany('Translation', 'labelId', 'titleLabelId');
}
}
?>
Translation:
<?php
class Translation extends Eloquent {
protected $table = 'translations';
public $timestamps = false;
protected $fillable = array('phrase', 'languageId', 'labelId');
public function language()
{
return $this->belongsTo('Language', 'languageId');
}
public function label()
{
return $this->belongsTo('Label', 'labelId');
}
}
?>
Label:
<?php
class Label extends Eloquent {
protected $table = 'label';
public $timestamps = false;
protected $fillable = array('key');
public function translations()
{
return $this->hasMany('Translation', 'labelId', 'id');
}
}
?>
There are three database tables with the following columns:
Discount:
id | titleLabelId
Translation:
id | languageId | labelId
Label:
id
The problem: I'd like to create a title (translation) and associate it with the discount. Here's what I've tried:
$discount = new Discount;
/*create a new label*/
$labelKey = Label::max('key') + 1;
$label = new Label(array('key' => $labelKey));
$label->save();
/*create a new title (and associate it with the label)*/
$title = new Translation(
array(
'phrase' => $input['title'],
'languageId' => 3,
'labelId' => $label->id
));
$title->save();
$discount->save();
$discount->titles()->save($title);
Apparently, the $discount->titles()->save($title); part doesn't work. The title is only attached to the discount if I do it manually: $discount->titleLabelId = $label->id. Is there a way to do it using the ORM?
In your Discount Model, do you have your relationship set up to use the proper table and foreign key?
class Discount extends Eloquent
{
public function titles()
{
return $this->belongsTo('Translation', 'translations', 'titleLabelId');
}
}
When trying to associate one model with another through a defined relationship in Eloquent, you should use the associate() method rather than the save() method.
$discount->titles()->associate($title);
Before this happens though, you should be sure to call the save() method on anything that has been altered or is new.

Accessing nested relationship with Laravel 4

I'm having trouble figuring out how to access a nested relationship within Laravel. The specific example I have is a Movie that has many entires in my Cast table which has one entry in my People table. These are my models:
MOVIE
class Movie extends Eloquent {
protected $primaryKey = 'movie_id';
protected $table = 'movie';
// Relationships
public function cast()
{
return $this->hasMany('MovieCast', 'movie_id');
}
}
MOVIECAST
class MovieCast extends Eloquent {
protected $table = 'movie_cast';
public function person()
{
return $this->hasOne('Person', 'person_id');
}
public function netflix()
{
return $this->belongsTo('Movie', 'movie_id');
}
}
PERSON
class Person extends Eloquent {
protected $primaryKey = 'person_id';
protected $table = 'people';
public function movieCast()
{
return $this->belongsTo('MovieCast', 'person_id');
}
}
In my controller I can access the cast (containing person_id and role_id) like so:
public function movie($id)
{
$movie = Movie::find($id);
$cast = $movie->cast();
return View::make('movie')->with(array(
'movie' => $movie,
'cast' => $cast
));
}
...but I don't know how to access the corresponding name field in my People table.
EDIT 1:
Using the classes exactly as defined below in #msturdy's answer, with the controller method above I try to render the person names like so inside my view:
#foreach($cast->person as $cast_member)
{{$cast_member->person->name}}
#endforeach
Doing this i get the error:
Undefined property: Illuminate\Database\Eloquent\Relations\HasMany::$person
I don't know if it makes a difference or not but I have no id field on my People table. person_id is the primary key.
It should be simple, once you have accessed the cast...
Route::get('movies', function()
{
$movie = Movie::find(1);
$cast = $movie->cast;
return View::make('movies')->with(array(
'movie' => $movie,
'cast' => $cast));
});
Note: at this point, $cast is an instance of the Collection class, not a single object of the MovieCast class, as the
relationship is defined with hasMany()
you can iterate over it in the View (in my case /app/views/movies.blade.php
#foreach($cast as $cast_member)
<p>{{ $cast_member->person->name }}</p>
#endforeach
Class definitions used for testing:
class Movie extends Eloquent {
protected $primaryKey = 'movie_id';
protected $table = 'movie';
public $timestamps = false;
// Relationships
public function cast()
{
return $this->hasMany('MovieCast', 'movie_id');
}
}
class MovieCast extends Eloquent {
protected $table = 'movie_cast';
public $timestamps = false;
public function person()
{
return $this->hasOne('Person', 'person_id');
}
}
class Person extends Eloquent {
protected $primaryKey = 'person_id';
protected $table = 'people';
public $timestamps = false;
public function movieCast()
{
return $this->belongsTo('MovieCast', 'person_id');
}
}

Categories