Laravel json response returns encrypted data - laravel

I'm using an Encryptable trait to encrypt my data for the a Room model.
RoomController (/rooms) returns the decrypted data but ApiRoomController (/api/rooms) does not.
How could I make it returns the decrypted data?
Encryptable Trait
trait Encryptable
{
public function getAttribute($key)
{
$value = parent::getAttribute($key);
if (in_array($key, $this->encryptable) && $value !== '' && $value !== null ) {
$value = Crypt::decrypt($value);
}
return $value;
}
public function setAttribute($key, $value)
{
if (in_array($key, $this->encryptable)) {
$value = Crypt::encrypt($value);
}
return parent::setAttribute($key, $value);
}
}
RoomController index function
public function index()
{
$rooms = Room::select('id', 'name')->get()->sortBy('name')->values()->all();
return view('rooms.index')->withRooms($rooms);
}
ApiRoomController index function
public function index()
{
$rooms = Room::select('id', 'name')->get()->sortBy('name')->values()->all();
return response()->json($rooms);
}

I found a way using API Resources:
php artisan make:resource Rooms --collection
Then in your app/Http/Resources/Rooms.php file:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
// more fields here
];
}
Then in your ApiRoomController.php file:
use App\Http\Resources\Rooms;
public function index()
{
$rooms = Room::select('id', 'name')->get()->sortBy('name')->values()->all();
return Rooms::collection($rooms);
}

Seems like #emotality came up with a good solution for this already...
However, the reason for this not working as you expected is because the underlying Model's toArray() / toJson() methods do not call the getAttribute() method in your trait.
This is important because the response()->json() method maps the given collection and calls the toJson() method on each model in order to prepare it for a response.
Therefore, you can also solve this by overwriting the toArray method in your model.
class Room extends Model
{
use Encryptable;
public function toArray()
{
return [
'id' => $this->id,
'name' => $this->name,
// ...
];
}
}

Related

Laravel FormRequest: client sends attribute with dots inside

Client sends something like this:
foo.1.bar=hello
in my FormRequest class I have tried:
public function rules()
{
return [
'foo.1.bar' => 'string|nullable' // does not work
'foo' => 'array|nullable' // does not work
'foo' => [ // does not work
'1' => [
'bar' => 'string|nullable'
]
]
Any idea how to handle this?
Try getting the value of the parameter manually and then validating it:
class YourRequest extends FormRequest
{
public function rules(): array
{
return [...];
}
public function withValidator(Validator $validator): void
{
$value = $this->get('foo.1.bar') ?: $this->get('foo_1_bar');
$validator->after(function (Validator $validator) use ($value) {
if ($value == null) {
$validator->errors()->add('foo.1.bar', 'Error Message.');
$validator->errors()->add('foo_1_bar', 'Error Message.');
}
});
}
}

Laravel default values for fields in FormRequest

Can I set a default value to a not-existing field in a FormRequest in Laravel?
For example, if a field called "timezone" does not exist in the incoming request, it get set to "America/Toronto".
Well I wrote a trait for this, which checks a function called 'defaults' exist in the form request it will replace the default values
trait RequestDefaultValuesTrait {
protected function prepareForValidation(){
// add default values
if( method_exists( $this, 'defaults' ) ) {
foreach ($this->defaults() as $key => $defaultValue) {
if (!$this->has($key)) $this->merge([$key => $defaultValue]);
}
}
}
}
the thing that you need to do is adding this trait to FormRequest class and then add a function like this:
protected function defaults()
{
return [
'country' => 'US',
'language' => 'en',
'timezone' => 'America/Toronto',
];
}
Being honest I don't link this method, but It works.
Try this
if(!$request->has('timezone') {
$request->merge(['timezone' =>'America/Toronto']);
}
I'm not so sure if you need to do it in this way, but if you want to:
class CreateUpdateDataFormRequest extends Request
{
public function authorize()
{
return true;
}
public function rules()
{
return [];
}
protected function getValidatorInstance()
{
$data = $this->all();
if(!isset($data['timezone'])) {
$data['timezone'] = 'America/Toronto';
$this->getInputSource()->replace($data);
}
// modify data before sending to validator
return parent::getValidatorInstance();
}
in request class add
public function prepareForValidation()
{
$this->mergeIfMissing([
'timezone' => 'America/Toronto'
]);
}

Filter by a Non-Null Value in Laravel Nova

I'm trying to implement a basic filter using Laravel Nova. The filter looks at the field "onboarded_at," which is a datetime field. If the attribute has a timestamp, they're onboarded, and if not, they aren't.
Here's my filter:
public function apply(Request $request, $query, $value)
{
return $query->where('onboarded_at', $value);
}
public function options(Request $request)
{
return [
'Onboarded' => !NULL, // How would I indicate a non-null value here?
'Not Onboarded' => NULL,
];
}
How would I indicate a non-null value in the options function?
Use the $value param to modify the query as desired:
public function apply(Request $request, $query, $value)
{
if($value) {
return $query->whewhereNotNull('onboarded_at');
}
return $query->whereNull('onboarded_at');
}
public function options(Request $request)
{
return [
'Onboarded' => 1,
'Not Onboarded' => 0,
];
}

Laravel Nova + Spatie Media library

Trying to use Laravel Nova with Spatie Media Library. I created upload field like this:
Image::make('Logo')
->store(function (Request $request, $model) {
$model->addMediaFromRequest('logo')->toMediaCollection('manufacturers');
}),
Seams ok, but Nova still trying to save file name to "logo" column in manufacturers table.
Original excample to customize this field was:
File::make('Attachment')
->store(function (Request $request, $model) {
return [
'attachment' => $request->attachment->store('/', 's3'),
'attachment_name' => $request->attachment->getClientOriginalName(),
'attachment_size' => $request->attachment->getSize(),
];
})
I found a work around by setting an empty mutator on the model. In your case it would be:
class Manufacturer extends Model implements HasMedia
{
use HasMediaTrait;
public function setLogoAttribute() {}
//...
}
Here's an example of my entire implementation. Note that currently with Nova 1.0.6, the preview() method is not working, it's returning the thumbnail() url.
App/GalleryItem
class GalleryItem extends Model implements HasMedia
{
use HasMediaTrait;
public function setImageAttribute() {}
public function registerMediaConversions(Media $media = null)
{
$this->addMediaConversion('thumbnail')
->fit(Manipulations::FIT_CROP, 64, 64);
$this->addMediaConversion('preview')
->fit(Manipulations::FIT_CROP, 636, 424);
$this->addMediaConversion('large')
->fit(Manipulations::FIT_CONTAIN, 1920, 1080)
->withResponsiveImages();
}
public function registerMediaCollections()
{
$this->addMediaCollection('images')->singleFile();
}
}
App/Nova/GalleryItem
class GalleryItem extends Resource
{
public static $model = 'App\GalleryItem';
public static $with = ['media'];
public function fields(Request $request)
{
return [
Image::make('Image')
->store(function (Request $request, $model) {
$model->addMediaFromRequest('image')->toMediaCollection('images');
})
->preview(function () {
return $this->getFirstMediaUrl('images', 'preview');
})
->thumbnail(function () {
return $this->getFirstMediaUrl('images', 'thumbnail');
})
->deletable(false);
];
}
}
As with Nova 3 (and Laravel 8) you need to return true from the fillUsing or store method:
File::make('Attachment')
->store(function (Request $request, $model) {
$model->addMediaFromRequest('logo')->toMediaCollection('manufacturers');
return true;
// This will tell nova that you have taken care of it yourself.
})
As soon as you return anything but true nova will assume, that it needs to save something to the database. This leads to an error if the field does not exist in db (as to expect with spatie-medialibrary) or it will overwrite your precious data if the field exists but serves another purpose.
Nova allows you to return true from the callback to indicate that the processing is complete and that it shouldn't set any attributes itself.
This is the code that runs the callback:
protected function fillAttribute(NovaRequest $request, $requestAttribute, $model, $attribute)
{
//...
$result = call_user_func($this->storageCallback, $request, $model);
if ($result === true) {
return;
}
if (! is_array($result)) {
return $model->{$attribute} = $result;
}
foreach ($result as $key => $value) {
$model->{$key} = $value;
}
}
So either true or any empty array will achieve the same thing, but personally feels clearer to do the former.
Image::make('Logo')
->store(function (Request $request, $model) {
$model->addMediaFromRequest('logo')->toMediaCollection('manufacturers');
return [];
}),
Maybe returning an empty array prevent nova from saving the name.

I am making admin panel in laravel 5.2, how to call function inside another function?

I want to know what is wrong in my code given below, I am make my code clean and problem arises since function is not called in another function.example my retrieve function is not called in form method...similary my saveintodatabase function in not called in form method?
there is my code
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Register;
class Admincontroller extends Controller
{
public function form(Request $request)
{
return $this->retrieve($request);
$register= new Register;
return $this->saveintodatabase($name,$phone,$email,$course,$address);
if($register->save())
{
return redirect()->route('displaydata');
}
else
{
echo "fail to insert";
}
}
public function display()
{
$records = Register::all();
return view('displaydata',['records' => $records]);
}
public function delete($id)
{
$records = Register::destroy($id);
$records = Register::all();
if(count($records) > 0)
{
return redirect()->route('displaydata');
}
else
{
echo "No record found";
}
}
public function update($id)
{
$records = Register::find($id);
return view('updatedata',['records' => $records]);
}
public function afterupdate(Request $request)
{
return $this->retrieve($request);
$id=$request->id;
$register = Register::find($id);
if($register->save())
{
//$this->display();
return redirect()->route('displaydata');
}
else
{
echo "fail to insert";
}
}
public function __construct(Request $request)
{
$this->validate($request,[
'name' =>'required',
'phone' => 'required',
'email' => 'required',
'course' => 'required',
'address' => 'required',
]);
}
private function saveintodatabase($name,$phone,$email,$course,$address)
{
$register->name=$name;
$register->phone=$phone;
$register->email=$email;
$register->course=$course;
$register->address=$address;
}
private function retrieve(Request $request )
{
$name=$request->name;
$phone=$request->phone;
$email=$request->email;
$course=$request->course;
$address=$request->address;
}
}
From your code if form function is called then retrieve function should be called However:
your retrieve function does not return anything or change any value for form function. How can you know if it is called. Set XDebugger could be good for you to check. Or simple put die in retrieve function to see if you are there or not.
Chances are your validatoin is failed in your constructor for this you also need to check by either debugger or die method
Laravel 5 has middleware, check if you are using it to cause you never reach to form function

Resources