Custom cast ignored after updating model - laravel

After I update my model, the cast is ignored and an old value that comes from classCastCache is shown.
I'm getting a Illuminate\Http\Testing\File instead of 'GETTING'.
Create a custom cast:
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class Image implements CastsAttributes
{
/**
* Cast the given value.
*
* #param \Illuminate\Database\Eloquent\Model $model
* #param string $key
* #param mixed $value
* #param array $attributes
* #return mixed
*/
public function get($model, $key, $value, $attributes)
{
return 'GETTING';
}
/**
* Prepare the given value for storage.
*
* #param \Illuminate\Database\Eloquent\Model $model
* #param string $key
* #param mixed $value
* #param array $attributes
* #return mixed
*/
public function set($model, $key, $value, $attributes)
{
return 'SETTING';
}
}
Apply it to a model:
<?php
declare(strict_types=1);
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Casts\Image;
class Product extends Model
{
use HasFactory;
/**
* Mass assignable attributes.
*
* #var string[]
*/
protected $fillable = [
'name',
'image',
];
/**
* Model's attribute casting.
*
* #var array
*/
protected $casts = [
'image' => Image::class,
];
}
Create a test for it:
/** #test */
public function it_can_change_product_image()
{
Storage::fake();
$business = Business::factory()->create();
$product = Product::factory()->for($business)->create();
$this->authenticate($business->owner); // Helper to authenticate with Sanctum. Ignore.
// The important part here, sending a fake image.
$response = $this->putJson(route('v1.products.update', $product), [
'image' => UploadedFile::fake()->image('example.jpg'),
]);
$response->assertOk(); // Among other tests that are hidden due demo purposes.
}
Create a Controller action to update the value of it.
/**
* Update the specified resource in storage.
*
* #param UpdateRequest $request
* #param Product $product
* #return ProductResource
*/
public function update(UpdateRequest $request, Product $product): ProductResource
{
// The custom request validator checks for:
// 'image' => ['sometimes', 'file', 'max:1024', 'image', 'mimes:jpg,bmp,png']
// This should update the product image based on the cast.
// The current DB value should be `SETTING`.
$product->update($request->validated());
// This should be `GETTING`.
// Instead, it is: Illuminate\Http\Testing\File
dd($product->image);
// a BAD workaround is to:
$product->refresh();
// This is the correct value `GETTING`.
dd($product->image);
}

Related

Laravel model attribute returning empty array

I have a model called Address. It has an 'additional' attribute. Although the attribute is filled, the model returns an empty array. I use an API resource to get the data.
This is the Address model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Address extends Model
{
use HasFactory;
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'addresses';
/**
* The primary key associated with the table.
*
* #var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model's ID is auto-incrementing.
*
* #var bool
*/
public $incrementing = true;
/**
* Indicates if the model should be timestamped.
*
* #var bool
*/
public $timestamps = false;
/**
* The database connection that should be used by the model.
*
* #var string
*/
protected $connection = 'solarium';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
//
];
/**
* The attributes that aren't mass assignable.
*
* #var array
*/
protected $guarded = [];
/**
* The model's default values for attributes.
*
* #var array
*/
protected $attributes = [
//
];
/**
* The attributes that should be cast.
*
* #var array
*/
protected $casts = [
//
];
public function addressable()
{
return $this->morphTo();
}
}
This is the Address API resource:
<?php
namespace App\Http\Resources;
use App\Models\Address;
use Illuminate\Http\Resources\Json\JsonResource;
class AddressResource extends JsonResource
{
/**
* The resource that this resource collects.
*
* #var string
*/
public $collects = Address::class;
/**
* Indicates if the resource's collection keys should be preserved.
*
* #var bool
*/
public $preserveKeys = true;
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'cep' => $this->cep,
'street' => $this->street,
'number' => $this->number,
'district' => $this->district,
'city' => $this->city,
'state' => $this->state,
'additional' => $this->additional,
];
}
}
As I said, the 'additional' attribute returns an empty array although it is filled with a string value.
The JsonResource has a public property named additional which has a default value of [], an empty array. That is what you are accessing since $this->additional is asking for that property. This is not forwarding that access to the model (resource) since this is an accessible property on this instance, (how PHP's OOP works).
You would probably have to access the underlying resource from the property of the JsonResource to then access that particular property (attribute):
$this->resource->additional

I get BadMethodCallException Call to undefined method App\Models\User::identifiableAttribute()

I get this error after clicking 'New Post' button the frontend of the app:
Posts view
Line from my log file:
[2020-09-27 14:41:03] local.ERROR: Call to undefined method App\Models\User::identifiableAttribute() {"exception":"[object] (BadMethodCallException(code: 0): Call to undefined method App\Models\User::identifiableAttribute() at C:\xampp\htdocs\backpack-demo\vendor\laravel\framework\src\Illuminate\Support\Traits\ForwardsCalls.php:50)
I am using Laravel 7 + Backpack CRDU generator
Posts Controller:
<?php
namespace App\Http\Controllers;
use App\Events\NewPost;
use App\Http\Requests\PostStoreRequest;
use App\Jobs\SyncMedia;
use App\Mail\ReviewPost;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class PostController extends Controller
{
/**
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index(Request $request)
{
$posts = Post::all();
return view('post.index', compact('posts'));
}
/**
* #param \App\Http\Requests\PostStoreRequest $request
* #return \Illuminate\Http\RedirectResponse
*/
public function store(PostStoreRequest $request)
{
$post = Post::create($request->validated());
Mail::to($post->author->email)->send(new ReviewPost($post));
SyncMedia::dispatch($post);
event(new NewPost($post));
$request->session()->flash('post.title', $post->title);
return redirect()->route('post.index');
}
}
Posts Model:
class Post extends Model
{
use CrudTrait;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'title',
'content',
'published_at',
'author_id',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'id' => 'integer',
'author_id' => 'integer',
];
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = [
'published_at',
];
public static function create(array $validated)
{
}
public function author()
{
return $this->belongsTo(User::class);
}
}
User model:
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
your have forgotten to use 'CrudTrait' in your User Model:
use Backpack\CRUD\app\Models\Traits\CrudTrait;
class User extends Authenticatable
{
use Notifiable,CrudTrait
.......
}

How to change Laravel-Nova action name?

Actually, I need to change the laravel nova action name like translate into different languages.
class PrintWithDetail extends Action
{
use InteractsWithQueue, Queueable, SerializesModels;
/**
* Perform the action on the given models.
*
* #param \Laravel\Nova\Fields\ActionFields $fields
* #param \Illuminate\Support\Collection $models
* #return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
$id = '';
//
foreach ($models as $model) {
$id = $model->id;
}
return Action::openInNewTab(route("print.work.order", [$id, 'type' => 'detail']));
}
}
Above action show PrintWithDetail as name.
But, I need to change PrintWithDetail into dutch.
Override the public property $name
/**
* The displayable name of the action.
*
* #var string
*/
public $name;
So in your example
class PrintWithDetail extends Action
{
use InteractsWithQueue, Queueable, SerializesModels;
public $name = "Print met detail";
/**
* Perform the action on the given models.
*
* #param \Laravel\Nova\Fields\ActionFields $fields
* #param \Illuminate\Support\Collection $models
* #return mixed
*/
public function handle(ActionFields $fields, Collection $models)
{
$id = '';
foreach ($models as $model) {
$id = $model->id;
}
return Action::openInNewTab(route("print.work.order", [$id, 'type' => 'detail']));
}
}
You can see all what you can change in the class the action extends in nova/src/Actions/Action.php
Update
If you need the name localized in other languages, you may override the method name return a string from the Laravel __() helper method
/**
* Get the displayable name of the action.
*
* #return string
*/
public function name()
{
return __('Print with details');
}

Laravel Nova Action Fields

EDIT:
in novas ActionEvent.php class i edited the two rows fields and exceptions for the forResourceUpdate function:
Whats actually strange now my resource is updated but there is nothing in the ACTION EVENT table.
public static function forResourceUpdate($user, $model)
{
return new static([
'batch_id' => (string) Str::orderedUuid(),
'user_id' => $user->getKey(),
'name' => 'Update',
'actionable_type' => $model->getMorphClass(),
'actionable_id' => $model->getKey(),
'target_type' => get_class($model),
'target_id' => $model->getKey(),
'model_type' => get_class($model),
'model_id' => $model->getKey(),
'fields' => 'test',
'status' => 'finished',
'exception' => 'test',
]);
}
so i just installed Nova set up some Resources, it shows everything fine, but when i try to update any of them, i get a Database error that i cant insert NULL into ACTION_EVENT.FIELDS.
https://nova.laravel.com/docs/1.0/actions/defining-actions.html#action-fields
The Doc says i can define some fields in the Actions fields function , but i didnt generate any Action class not i want to. I think its some sort of default action logging?
Can i turn it off, or do i have to add some Fields somewhere?
<?php
namespace App\Nova;
use Laravel\Nova\Fields\ID;
use Illuminate\Http\Request;
use Laravel\Nova\Http\Requests\NovaRequest;
class Chain extends Resource
{
public static $category = "Stammdaten";
/**
* The model the resource corresponds to.
*
* #var string
*/
public static $model = 'App\Model\System\Partner\Chain';
/**
* The single value that should be used to represent the resource when being displayed.
*
* #var string
*/
public static $title = 'id';
/**
* The columns that should be searched.
*
* #var array
*/
public static $search = [
'id',
];
/**
* Get the fields displayed by the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function fields(Request $request)
{
return [
ID::make()->sortable(),
];
}
/**
* Get the cards available for the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function cards(Request $request)
{
return [];
}
/**
* Get the filters available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function filters(Request $request)
{
return [];
}
/**
* Get the lenses available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function lenses(Request $request)
{
return [];
}
/**
* Get the actions available for the resource.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function actions(Request $request)
{
return [];
}
}
And The Model:
class Chain extends Model
{
use SoftDeletes;
use ObservantTrait;
use TranslationTrait;
protected $primaryKey = 'partner_chain_id';
protected $table = 'tbl_prm_partner_chain';
public $sequence = 'seq_partner_chain_id';
const CREATED_BY = 'insert_user';
const CREATED_AT = 'insert_timestamp';
const UPDATED_BY = 'update_user';
const UPDATED_AT = 'update_timestamp';
const DELETED_BY = 'delete_user';
const DELETED_AT = 'delete_timestamp';
protected $fillable = ['name_text', 'partner_type_id'];
protected $dates = ['delete_timestamp', 'insert_timestamp'];
thats all its quit simple, thats why i have no Clue right now where i am going wrong.

SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value

I have created a new column in my database through a migration as below :
public function up(){
Schema::table('complains', function (Blueprint $table) {
$table->integer('user_id')->after('id');
});
}
When I fill in my form to post data into the database, I get the error below :
SQLSTATE[HY000]: General error: 1364 Field 'user_id' doesn't have a default value (SQL: insert into complains (title, body, name, regnumber, updated_at, created_at) values (Testing user id, Testing user id, John, cs-282-2145/2010, 2017-06-08 18:47:53, 2017-06-08 18:47:53))
How do I fix this?
I've had a similar issue with User registration today and I was getting a
SQLSTATE[HY000]: General error: 1364 Field 'password' doesn't have a default value (SQL: insert into users
I fixed it by adding password to my protected $fillable array and it worked
protected $fillable = [
'name',
'email',
'password',
];
I hope this helps.
Probably you aren't sending a value for user_id
Other possibility, yout can "transform" the column user_id nullable
Here is the code for my controller :
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Complain;
use Illuminate\Support\Facades\Redirect;
use Session;
use Auth;
class ComplainController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return view('home');
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, array(
'title' => 'required|max:255',
'body' => 'required'
));
$complain = new Complain;
$complain->user_id = Auth::user()->id;
$complain->title = $request->title;
$complain->body = $request->body;
$complain->save();
Session::flash('success', 'Your complain was sent to the operator, please wait for feedback.');
return redirect::back();
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$complain = Complain::find($id);
return view('admin')->withPost($complain);
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
This is my model :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Complain extends Model
{
}

Resources