Well, I think the title explains most of it. Lets get right into it!
Blank Model:
class Blank extends Eloquent
{
protected $table = 'blanks';
protected $softDelete = true;
protected $hidden = array();
/**
* Get associated jobs.
*
* #return mixed
*/
public function jobs()
{
return $this->belongsToMany('Job')->withPivot('status', 'inventory', 'sizes', 'mill', 'po', 'location', 'ordered_at', 'expected_at', 'note')->withTimestamps();
}
/**
* Blanks sizes accessor
*
* #return object
*/
public function getSizesAttribute($value)
{
return json_decode($this->pivot->sizes);
}
/**
* Blanks sizes mutator
*
* #return void
*/
public function setSizesAttribute($value)
{
$this->pivot->attributes['sizes'] = json_encode($this->pivot->sizes);
}
}
Job Model:
class Job extends Eloquent
{
protected $table = 'jobs';
protected $softDelete = true;
protected $hidden = array();
/**
* Get associated blank.
*
* #return mixed
*/
public function blanks()
{
return $this->belongsToMany('Blank')->withPivot('status', 'inventory', 'sizes', 'mill', 'po', 'location', 'ordered_at', 'expected_at', 'note')->withTimestamps();
}
/**
* Blanks sizes accessor
*
* #return object
*/
public function getSizesAttribute($value)
{
return json_decode($this->pivot->sizes);
}
/**
* Blanks sizes mutator
*
* #return void
*/
public function setSizesAttribute($value)
{
$this->pivot->attributes['sizes'] = json_encode($this->pivot->sizes);
}
}
Attaching Code:
$job->blanks()->attach($blank->id,[
'status' => Input::get('status'),
'inventory' => Input::get('inventory'),
//'sizes' => $sizes,
'mill' => Input::get('mill'),
'po' => Input::get('po'),
'location' => Input::get('location'),
'ordered_at' => Carbon::parse(Input::get('ordered_at'))->format('Y-m-d H:i:s'),
'expected_at' => Carbon::parse(Input::get('expected_at'))->format('Y-m-d H:i:s'),
'note' => Input::get('note'),
]);
The mutator is not being called at all.. Any ideas?
Seems like that not possible to do this through ::attach() method.
But maybe you would like to use 'Defining A Custom Pivot Model'
public function newPivot(Model $parent, array $attributes, $table, $exists)
{
return new YourCustomPivot($parent, $attributes, $table, $exists);
}
So, you can define your own pivot class with mutators:
class BlankJobPivot extends Eloquent
{
// ...
/**
* Blanks sizes accessor
*
* #return object
*/
public function getSizesAttribute($value)
{
return json_decode($value);
}
/**
* Blanks sizes mutator
*
* #return void
*/
public function setSizesAttribute($value)
{
$this->attributes['sizes'] = json_encode($value);
return $value; // return for multiple assignment statement: $arr = $pivot->sizes = array(12, 23, 34);
}
}
And than you can use getters:
$blank->jobs[$i]->pivot->sizes; // - ::getSizesAttribute() will called ( I hope :) )
And maybe you will find a way to save / attach through the setSizesAttribute mutator.
Good luck.
Related
I am using Laravel 8 and I was wondering if there is anyway to automatically delete original image after it has converted in Spatie Media Library? it's currently taking up my storage space so I want to be able to delete original images.
here's the model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia\HasMedia;
use Spatie\MediaLibrary\HasMedia\HasMediaTrait;
use Spatie\MediaLibrary\Models\Media;
class Profile extends Model implements HasMedia
{
use HasMediaTrait, softDeletes;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'user_id',
'theme_id',
'name',
'username',
'location',
'bio',
'views',
];
protected $appends = [
'avatar_url',
'route',
];
protected $with = [
'links',
'theme',
];
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'username';
}
/**
* #return mixed
*/
public function getAvatarUrlAttribute()
{
if ($this->hasMedia('avatar')) {
return $this->getFirstMedia('avatar')->getUrl('cropped');
}
return asset('/images/avatar.png');
}
/**
* #return mixed
*/
public function getRouteAttribute()
{
return route('profiles.show', $this);
}
/**
* #return mixed
*/
public function getKeywordsAttribute()
{
$keywords = $this->links
->map(function ($link) {
return "{$link->name} {$this->name}";
})
->toArray();
$keywords[] = $this->name;
return implode(',', $keywords);
}
/**
* Get all of the profile's links.
*
* #param string $order
*
* #return \Illuminate\Database\Eloquent\Relations\MorphMany
*/
public function links($order = 'asc')
{
return $this->morphMany(Link::class, 'linkable')->orderBy('order', $order);
}
/**
* Get profile theme.
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function theme()
{
return $this->belongsTo(Theme::class);
}
/**
*
*/
public function viewed()
{
$this->increment('views');
}
/**
*
*/
public function registerMediaCollections()
{
$this->addMediaCollection('avatar')->singleFile();
}
/**
* #param Media|null $media
*
* #throws \Spatie\Image\Exceptions\InvalidManipulation
*/
public function registerMediaConversions(Media $media = null)
{
$this->addMediaConversion('cropped')
->crop(Manipulations::CROP_CENTER, 200, 200)
->nonQueued()
->performOnCollections('avatar');
}
}
Anyone know any method to do this so I can save storage space?
I can not delete a row using a simple eloquent query. Even when I am using eloquent can not get the data from DB. I am getting null. But, in DB query method at least I am getting data but can not delete then. Following is my code:
DB::transaction(function () use ($lead, $comment, $request) {
$lead->save();
$lead->comments()->save($comment);
if ($request->deleteAppointment) {
$calendarEvent = DB::table('calendar_events')->where('id', $request->appointmentId)->first(); // I am getting data here.
$calendarEvent = CalendarEvent::find($request->appointmentId); // But, here I am getting null, don't know why!
if ($calendarEvent != null) {
$calendarEvent->delete();
}
}
My goal is to get the data using Eloquent and then Delete from database.
update:
My Database Table
CalendarEvent.php model
class CalendarEvent extends Model
{
use SoftDeletes;
/**
* #var array
*/
protected $casts = [
'event_begin' => 'datetime',
'event_end' => 'datetime',
'options' => 'array',
];
/**
* #var array
*/
protected $guarded = [
'id',
];
/**
* #return mixed
*/
public function users()
{
return $this->morphedByMany(User::class, 'eventable');
}
/**
* #return mixed
*/
public function attendees()
{
return $this->morphedByMany(User::class, 'eventable')->withPivotValue('role', 'atendee');
}
/**
* #return mixed
*/
public function companies()
{
return $this->morphedByMany(Company::class, 'eventable')->withPivotValue('role', 'company');
}
/**
* #return mixed
*/
public function invitees()
{
return $this->morphedByMany(User::class, 'eventable')->withPivotValue('role', 'invitee');
}
/**
* #return mixed
*/
public function leads()
{
return $this->morphedByMany(Lead::class, 'eventable')->withPivotValue('role', 'lead');
}
}
Why not just:
CalendarEvent::where('id', $request->appointmentId)->delete();
Also, check the deleted_at column. If that is not null, then the select will return null, unless you add the ->withTrashed() method.
When using Eloquent objects, the SoftDelete trait is used, when using DB:: directly, then the SoftDelete trait is not used.
Right now I try this query with eloquent:
'MentorId' => $employee->intern(true)->mentor(true)->MentorId,
And in my employee and intern model I've got this:
Intern
/**
* #return mixed
*/
public function intern($withTrashed = false)
{
if($withTrashed == true)
{
return $this->belongsTo(internModel::class, 'InternId')->withTrashed();
}
return $this->belongsTo(internModel::class,'InternId');
}
Mentor
/**
* #return mixed
*/
public function mentor($withTrashed = false)
{
if($withTrashed == true)
{
return $this->belongsTo(mentorModel::class, 'MentorId')->withTrashed();
}
return $this->belongsTo(mentorModel::class,'MentorId');
}
But it crashes:
BadMethodCallException in Builder.php line 2148:
Call to undefined method Illuminate\Database\Query\Builder::mentor()
How could I fix this?
--EDIT--
Employee
<?php
namespace App\src\employee;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\src\department\Department as departmentModel;
use App\src\employee\Employee as employeeModel;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\src\intern\Intern as internModel;
use App\src\mentor\Mentor as mentorModel;
use App\src\employee\Role as roleModel;
class Employee extends Authenticatable
{
use SoftDeletes;
use EmployeeServiceTrait;
/**
* table name
*/
protected $table = 'employee';
/**
* Mass assignment fields
*/
protected $fillable = ['RoleId', 'DepartmentId', 'InternId', 'FirstName', 'LastName', 'Bio','api_token', 'email', 'LinkedIn', 'password', 'Address', 'Zip', 'City', 'ProfilePicture', 'BirthDate', 'StartDate', 'EndDate', 'Suspended','LinkedIn'];
/**
* Primarykey
*/
protected $primaryKey = 'EmployeeId';
/**
* Deleted_at
*/
protected $dates = ['deleted_at'];
/**
* #return mixed
*/
public function role()
{
return $this->belongsTo(roleModel::class,'RoleId');
}
/**
* #return mixed
*/
public function intern($withTrashed = false)
{
if($withTrashed == true)
{
return $this->belongsTo(internModel::class, 'InternId')->withTrashed();
}
return $this->belongsTo(internModel::class,'InternId');
}
/**
* #return mixed
*/
public function department()
{
return $this->belongsTo(departmentModel::class,'DepartmentId');
}
/**
* #return mixed
*/
public function mentor()
{
return $this->belongsTo(mentorModel::class,'MentorId');
}
/**
* #return mixed
*/
public function employees()
{
return $this->hasManyThrough(employeeModel::class,departmentModel::class,'CompanyId','DepartmentId');
}
/**
* #param $role
* #return bool
*/
public function hasRole($role)
{
if(strtolower($this->role->RoleName) == strtolower($role))
{
return true;
}
return false;
}
}
The problem you have is that any Eloquent relationship object is actually an instance of Relation. This means when you create relationships you actually return a collection (instance of Builder); Hense your error:
BadMethodCallException in Builder.php line 2148:
Call to undefined method Illuminate\Database\Query\Builder::mentor()
The simple solution, without any modification to your code would be something like:
'MentorId' => $employee->intern(true)->first()->mentor(true)->first()->MentorId;
However, you could use overloading like the following:
'MentorId' => $employee->intern->mentor->MentorId;
Although this will NOT include your withTrashed. You can however tweak your relationship to something like:
public function intern($withTrashed = false)
{
$relation = $this->belongsTo(internModel::class, 'InternId');
if($withTrashed == true)
{
return $relation->withTrashed()->first();
}
return $relation->first();
}
But I wouldn't advise this because later on if you try using things like WhereHas you will get errors. That said, another way would be to do something along the following lines:
public function intern()
{
return $this->belongsTo(internModel::class, 'InternId');
}
Then get trashed like:
'MentorId' => $employee->intern()->withTrashed()->first()->mentor()->withTrashed()->first()->MentorId;
Try as below as per laravel guide. Keep in mind that parent model must have hasOne/hasMany method and child model must have belongsTo method.
Intern
/**
* #return mixed
*/
public function intern($withTrashed = false)
{
if($withTrashed == true)
{
return $this->hasOne('App\Intern', 'InternId')->withTrashed();
}
return $this->hasOne('App\Intern','InternId');
}
Employee
/**
* #return mixed
*/
public function intern($withTrashed = false)
{
if($withTrashed == true)
{
return $this->belongsTo('App\Intern', 'InternId')->withTrashed();
}
return $this->belongsTo('App\Intern','InternId');
}
Note: Same for all other models.
i'm trying to test a simple class. I'm following this tutorial( http://code.tutsplus.com/tutorials/testing-laravel-controllers--net-31456 ).
I have this error, while running tests:
Method Mockery_0_App_Interfaces_MealTypeRepositoryInterface::getValidator() does not exist on this mock object
Im using repository structure. So, my controller calls repository and that returns Eloquent's response.
I'm relatively new in php and laravel. And I've started learning to test a few days ago, so I'm sorry for that messy code.
My test case:
class MealTypeControllerTest extends TestCase
{
public function setUp()
{
parent::setUp();
$this->mock = Mockery::mock('App\Interfaces\MealTypeRepositoryInterface');
$this->app->instance('App\Interfaces\MealTypeRepositoryInterface' , $this->mock);
}
public function tearDown()
{
Mockery::close();
}
public function testIndex()
{
$this->mock
->shouldReceive('all')
->once()
->andReturn(['mealTypes' => (object)['id' => 1 , 'name' => 'jidlo']]);
$this->call('GET' , 'mealType');
$this->assertViewHas('mealTypes');
}
public function testStoreFails()
{
$input = ['name' => 'x'];
$this->mock
->shouldReceive('getValidator')
->once()
->andReturn(Mockery::mock(['fails' => true]));
$this->mock
->shouldReceive('create')
->once()
->with($input);
$this->call('POST' , 'mealType' , $input ); // this line throws the error
$this->assertRedirectedToRoute('mealType.create');//->withErrors();
$this->assertSessionHasErrors('name');
}
}
My EloquentMealTypeRepository:
Nothing really interesting.
class EloquentMealTypeRepository implements MealTypeRepositoryInterface
{
public function all()
{
return MealType::all();
}
public function find($id)
{
return MealType::find($id);
}
public function create($input)
{
return MealType::create($input);
}
public function getValidator($input)
{
return MealType::getValidator($input);
}
}
My eloquent implementation:
Nothing really interresting,too.
class MealType extends Model
{
private $validator;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'meal_types';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['name'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = [];
public function meals()
{
return $this->hasMany('Meal');
}
public static function getValidator($fields)
{
return Validator::make($fields, ['name' => 'required|min:3'] );
}
}
My MealTypeRepositoryInterface:
interface MealTypeRepositoryInterface
{
public function all();
public function find($id);
public function create($input);
public function getValidator($input);
}
And finally, My controller:
class MealTypeController extends Controller {
protected $mealType;
public function __construct(MealType $mealType)
{
$this->mealType = $mealType;
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
$mealTypes = $this->mealType->all();
return View::make('mealTypes.index')->with('mealTypes' ,$mealTypes);
}
/**
* Show the form for creating a new resource.
*
* #return Response
*/
public function create()
{
$mealType = new MealTypeEloquent;
$action = 'MealTypeController#store';
$method = 'POST';
return View::make('mealTypes.create_edit', compact('mealType' , 'action' , 'method') );
}
/**
* Validator does not work properly in tests.
* Store a newly created resource in storage.
*
* #return Response
*/
public function store(Request $request)
{
$input = ['name' => $request->input('name')];
$mealType = new $this->mealType;
$v = $mealType->getValidator($input);
if( $v->passes() )
{
$this->mealType->create($input);
return Redirect::to('mealType');
}
else
{
$this->errors = $v;
return Redirect::to('mealType/create')->withErrors($v);
}
}
/**
* Display the specified resource.
*
* #param int $id
* #return Response
*/
public function show($id)
{
return View::make('mealTypes.show' , ['mealType' => $this->mealType->find($id)]);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return Response
*/
public function edit($id)
{
$mealType = $this->mealType->find($id);
$action = 'MealTypeController#update';
$method = 'PATCH';
return View::make('mealTypes.create_edit')->with(compact('mealType' , 'action' , 'method'));
}
/**
* Update the specified resource in storage.
*
* #param int $id
* #return Response
*/
public function update($id)
{
$mealType = $this->mealType->find($id);
$mealType->name = \Input::get('name');
$mealType->save();
return redirect('mealType');
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return Response
*/
public function destroy($id)
{
$this->mealType->find($id)->delete();
return redirect('mealType');
}
}
That should be everything. It's worth to say that the application works, just tests are screwed up.
Does anybody know, why is that happening? I cant see a difference between methods of TestCase - testIndex and testStoreFails, why method "all" is found and "getValidator" is not.
I will be thankful for any tips of advices.
Perhaps an aside, but directly relevant to anyone finding this question by its title:
If:
You are getting the error BadMethodCallException: Method Mockery_0_MyClass::myMethod() does not exist on this mock object, and
none of your mocks are picking up any of your subject's methods, and
your classes are being autoloaded, (e.g. using composer)
then before making your mock object, you need to force the loading of that subject, by using this line of code:
spl_autoload_call('MyNamespace\MyClass');
Then you can mock it:
$mock = \Mockery::mock('MyNamespace\MyClass');
In my PHPUnit tests, I often put that first line into the setUpBeforeClass() static function, so it only gets called once and is isolated from tests being added/deleted. So the Test class looks like this:
class MyClassTest extends PHPUnit_Framework_TestCase {
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
spl_autoload_call('Jodes\MyClass');
}
public function testIt(){
$mock = \Mockery::mock('Jodes\MyClass');
}
}
I have forgotten about this three times now, each time spending an hour or two wondering what on earth the problem was!
I have found a source of this bug in controller.
calling wrong
$v = $mealType->getValidator($input);
instead of right
$v = $this->mealType->getValidator($input);
Every time I try to instantiate a certain model and use it, like for example:
$categories = Model::factory('category')->by_sale($id)->find_all();
I get a weird error. If i have i bootstrap in kohana::init set 'errors' => TRUE,the error is: Could not execute Model_Category::__construct() else i get only a warning Warning: array_keys() expects parameter 1 to be array, null given in /application/classes/model.php on line 42
meaning here:
private function _get_real_property_name($property)
{
if (isset($this->_belongs_to[$property]) OR
isset($this->_has_one[$property]) OR
isset($this->_has_many[$property]))
return $property;
$column_prefix = $this->_table_name . '_';
$property_prefix = substr($property, 0, strlen($column_prefix));
if ($property_prefix != $column_prefix)
{
$prefixed_property = $column_prefix . $property;
if (in_array($prefixed_property, array_keys($this->table_columns())))
{
return $prefixed_property;
}
}
return $property;
}
The category model looks like this:
class Model_Category extends Model {
/**
* #see ORM::_table_name
*
* #var array
*/
protected $_table_name = 'category';
/**
* #see ORM::_primary_key
*
* #var array
*/
protected $_primary_key = 'category_id';
/**
* #see ORM::_belongs_to
*
* #var array
*/
protected $_belongs_to = array(
'parent' => array('model' => 'category', 'foreign_key' => 'category_category'),
'sale' => array('foreign_key' => 'category_sale')
);
/**
* #see ORM::_has_many
*
* #var array
*/
protected $_has_many = array(
'products' => array('model' => 'product', 'foreign_key' => 'product_category')
);
/**
* Adds the 'top_level' condition to the query
*
* #return Model_Sale
*/
public function top_level()
{
return $this->where('category_category', '=', 0);
}
/**
* Adds the 'by_sale' condition to the query
*
* #return Model_Sale
*/
public function by_sale($sale_id)
{
return $this->where('category_sale', '=', $sale_id);
}
public function __get($property)
{
if ($property == 'siblings')
{
return $this->where('category_sale', '=', $this->sale->id)
->where('category_category', '=', $this->category_category);
}
if ($property == 'children')
{
return $this->where('category_category', '=', $this->pk());
}
return parent::__get($property);
}
} // End Model_Category
Thank you!
I don't recognize the $this->table_columns(). When looking at http://kohanaframework.org/3.0/guide/api/Model I don't see it there either.
Have you added this method to the Model class yourself? I'm guessing it's not returning the proper type.
I am not familiar with Kohana, but it is quite resembling with Codeigniter. You can try this in your model that extends model class:
function __construct(){
parent::Model();
}
(eventually pass the parameters that you have)
Try replacing $this->table_columns() by $this->table_columns (in _get_real_property_name()) because table_columns seems to be a variable, not a method :3