Revisionable presenters not working - laravel

First I find a ticket.
$tick = App\Tickets::find(14);
Then I find a revision for the same:
$rev = $tick->latestRevision;
But it gives me a error:
App\Presenters\Revisions\Tickets #0000000021ba4aef0000000179e23051 {}
When I see in database, the revisions table is updated with a revision.
And this is my Presenters code:
namespace App\Presenters\Revisions;
use Sofa\Revisionable\Laravel\Presenter;
class Tickets extends Presenter {
protected $passThrough = [
'stage_id' => 'stage.stage_name',
];
protected $actions = [
'created' => 'Created at',
'updated' => 'Updated at',
'deleted' => 'Deleted',
'restored' => 'Restored',
];
}
So this is my relation from Tickets model.
public function stage() {
return $this->hasOne('App\Stages');
}
And I used stage.stage_name in passThrough, but still there is no result.
Also, when I do $revision->old('stage_id'); , I get null
I am using this package: https://github.com/jarektkaczyk/revisionable

That's not an error, that is the tinker output showing you your App\Presenters\Revisions\Tickets object.
Do a $rev->getDiff() and it should work fine.

Related

How to use relationships in laravel 8

my question has two parts
Firstly, My if statement is not working. My if statement is as followed:
if ($request->is_published) {
$resources_page->published_at = now();
}
This is stored in my controller, I have a model for this and it is as followed:
public function is_published()
{
return $this->published_at !== null;
}
It is meant to check whether my checkbox is checked and return the timestamp, I have it cast in my model like followed:
protected $casts = [
'published_at' => 'datetime',
];
And in my view:
#include('components.form.input-checkbox', [
'label' => 'Publish?',
'form_object' => 'page',
'name' => 'is_published'
])
Could anyone elude to the answer?
Secondly, when trying to sync, it is not storing to my resources_category_resources_page table
In my controller, i have the following code
$resources_page->resources_categories()->sync(
ResourcesCategory::whereIn('slug', $request->resources_categories)->pluck('id')
);
In my model I have the relationships declared properly, so I don't know why its not storing?

laravel DB update get changes column

i want to save log of changes when i update something on the database.
there is elegant way to get the column that will be updated (just if there is change).
i want to save the old column value in log..
for example:
$updateUser = DB::table('users')->where('id','1')->update(array('email' => 'new#email.com', 'name' => 'my new name'));
from this i want to get back the old email was in database (if changed) and the old name (again, only if changed)
thanks!
As others have mentioned, Eloquent is a great way to go if using Laravel. Then you can tap directly into Laravel's events using Observers. I have used a method very similar to what is below. Of course, you would need to set up Models for User and AuditLog.
See more info regarding Observers.
https://laravel.com/docs/5.8/eloquent#observers
In Controller Method
$user = User::find(1);
$user->update([
'email' => 'new#email.com',
'name' => 'my new name'
]);
App/Providers/EventServiceProvider.php
class EventServiceProvider extends ServiceProvider
{
// ...
public function boot()
{
User::observe(UserObserver::class);
}
}
App/Observers/UserObserver.php
class UserObserver
{
/**
* The attributes to exclude from logging.
*
* #var array
*/
protected $except = [
'created_at',
'updated_at'
];
/**
* The attributes to mask.
*
* #var array
*/
protected $masked = [
'password',
];
/**
* Listen for model saved event.
*
* #var array
*/
public function saved($model)
{
// search for changes
foreach ($model->getChanges() as $key => $new_value) {
// get original value
$old_value = $model->getOriginal($key);
// skip type NULL with empty fields
if ($old_value === '' && $new_value === null) {
continue;
}
// attribute not excluded and values are different
if (!in_array($key, $this->except) && $new_value !== $old_value) {
// mask designated fields
if (in_array($key, $this->masked)) {
$old_value = '********';
$new_value = '********';
}
// create audit log
AuditLog::create([
'user_id' => auth()->user()->id,
'model_id' => $model->id,
'model' => (new \ReflectionClass($model))->getShortName(),
'action' => 'update',
'environment' => config('app.env'),
'attribute' => $key,
'old_value' => $old_value,
'new_value' => $new_value,
]);
}
}
}
}
I hope this helps!
EDIT: See comment regarding update.
I will suggest 2 options:
1) to use the Eloquent model on every changes,
and then to use the existing methods like :
model->isDirty()
model->getChanges()
you can implement it on the model life cycle of updating / updated events listeners
more information and example you can see here:
https://laravel.com/docs/5.8/events
https://medium.com/#JinoAntony/10-hidden-laravel-eloquent-features-you-may-not-know-efc8ccc58d9e
https://laravel.com/api/5.3/Illuminate/Database/Eloquent/Model.html
2) if you want to log changes even if you are running regular queries and not only via model life cycle,
you can use MySql Triggers on every table updates and then to check OLD vs NEW and insert directly to the log changes db
more information you can find here:
https://dev.mysql.com/doc/refman/8.0/en/trigger-syntax.html
MySQL Trigger after update only if row has changed
Why not just something like this:
$changeArr = ['email' => 'new#email.com', 'name' => 'my new name'];
$id = 1;
$table = 'users';
foreach($changeArr as $key => $value){
DB::table('updateTable')->insert(['table' => $table, 'id' => $id, 'col' => $key, 'oldVal' => $value]);
}
$updateItem = DB::table($table)->where('id', $id)->update($changeArr);
Check for the changed values and update accordingly, saving the old values to log table if changed
$newData = ['email' => 'new#email.com', 'name' => 'my new name'];
$user = App\User::find(1);
$log = [];
if ($user->email != $newData['email']) {
$log['user_id'] = $user->id;
$log['email'] = $user->email;
$user->email = $newData['email'];
} elseif ($user->name != $newData['name']) {
$log['name'] = $user->name;
$user->name = $newData['name'];
$logged = DB::table('log')->insert($log);
}
$updateUser = $user->save();
//try this. hpe it helps out:
function Update(Request $request, $id)
{
$dbrecord = DB::table('users')->where('id',$id)->first();
$oldemail = $dbrecord->email;
$oldname = $dbrecord->name;
if(($oldemail==$request->input('email'))&&($oldname==$request->input('name')))
{
//do nothing
}
elseif(($oldemail!=$request->input('email'))or($oldname!=$request->input('name')))
{
$updateUser = DB::table('users')->where('id',$id)->update(array('email' => $request->input('email'), 'name' => $request->input('name')));
if($updateUser)
{
DB::table('log')->where('id',$id)->insert(array('email' => $oldemail, 'name' => $oldname));
}
}
}

Laravel avoid duplicate entry from model

I'm building a Laravel API. I have a models called Reservations. I want to avoid that a user creates two reservations for the same product and time period.
I have the following:
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
Edit after comments:
I'm also using validation
$validator = Validator::make($request->all(), [
'listing_id' => 'required|exists:listings,id',
'user_id_from' => 'required|exists:users,id',
'start_date' => 'required|date_format:"Y-m-d"|after:today',
'end_date' => 'required|date_format:"Y-m-d"|after:start_date'
]);
if ($validator->fails()) {
return response()->json(['error' => 'Validation failed'], 403);
}
Validation is working properly.
End of Edit
In my model I have casted the start_date and end_date as dates.
class Reservation extends Model
{
protected $fillable = ['listing_id', 'start_date', 'end_date'];
protected $dates = [
'start_date',
'end_date'
];
....
....
Documentation says:
The firstOrCreate method will attempt to locate a database record
using the given column / value pairs
However I notice that I'm still able to insert entries with the same attributes.
Any idea what I'm doing wrong or suggestions to fix it?
Probably there's a better way than this, but you can create an static method on Reservation to do this, like:
public static function createWithRules($data) {
$exists = $this->where('product_id', $data['product_id'])->whereBetween(*date logic that i don't remember right now*)->first();
if(!$exists) {
* insert logic *
} else {
* product with date exists *
}
}
So you can call Reservation::createWithRules($data)
You can achieve this using Laravel's built in ValidateRequest class. The most simple use-case for this validation, is to call it directly in your store() method like this:
public function store(){
$this->validate($request, [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
], $this->messages);
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
}
With this, you're validating users $request with by saying that specified columns are required and that they need to be unique, in order for validation to pass.
In your controller, you can also create messages function to display error messages, if the condition isn't met.
private $messages = [
'listing_id.required' => 'Listing_id is required',
'title.unique' => 'Listing_id already exists',
//... and so on
];
You can also achieve this by creating a new custom validation class:
php artisan make:request StoreReservation
The generated class will be placed in the app/Http/Requests directory. Now, you can add a few validation rules to the rules method:
public function rules()
{
return [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
];
}
All you need to do now is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:
public function store(StoreReservation $request)
{
// The incoming request is valid...
// Retrieve the validated input data...
$validated = $request->validated();
}
If you have any additional question about this, feel free to ask. Source: Laravel official documentation.

Get all the related fields in one json response

i want to make an api with laravel and i want to get all the fields like the following format
projects: [
{
id,
name,
logo,
thumbnail,
phases: [{
id,
name,
date,
views: [{
id,
name,
thumbnail,
images: [{
id,
path: [ 5 urls for the various images ]
date
}]
}]
}]
}
]
my database model like the following
- projects -> hasmany phases
- phases -> hasmany view
- views -> hasmany images
the model like the following
class Project extends \Eloquent {
// Add your validation rules here
public static $rules = [
'title' => 'required',
];
// Don't forget to fill this array
protected $fillable = [ 'title', 'desc' ];
public function phases() {
return $this->hasMany('Phase');
}
}
class Phase extends \Eloquent {
protected $fillable = [ 'title', 'from', 'to', 'project_id' ];
public static $rules = [
'title' => 'required',
'from' => 'required',
'to' => 'required'
];
public function views() {
return $this->hasMany( 'View' );
}
public function project(){
return $this->belongsTo('Project');
}
}
class View extends \Eloquent {
// Add your validation rules here
public static $rules = [
'title' => 'required',
'image_path' => 'required'
];
// Don't forget to fill this array
protected $fillable = [ 'title', 'image_path', 'caption', 'view_date', 'phase_id' ];
public function phase(){
return $this->belongsTo('Phase');
}
}
How can i get all the json in the index response, i use the following but not getting the same format
Project::all();
You have to eager load the datasets:
Project::with('phases.views','phases.images')->get();
Looks like you don't have the images in your model as a related model, but you do have projects. But your json example shows images and not projects, is this right?
Relationships aren't loaded by default when retrieving a collection (this is called lazy loading). But you can load them by using the with() method (this is called eager loading):
Project:all()->with('phases')->get();
You can also chain nested relationships with dot notation:
Project:all()->with('phases.views')->get();

Capitalized column name to retrieve data in Laravel 4

Overview:
I have this table called User
Notice that most of the column names are on StudlyCaps like every word has been capitalized.
Now, one of the problem that I've been experiencing is of course when logging in. It's mostly like Laravel doesn't like capitalized column names and such.
Here's my User Model I'll just put the relevant parts regarding on my problem.
class User extends Eloquent implements UserInterface, RemindableInterface {
protected $primaryKey = "UserID";
protected $fillable = array('Username', 'Password', 'Active');
protected $table = 'Users';
}
And here's my method where the user starts to log in.
public function postLogin() {
$validator = Validator::make(Input::all(),
array(
'username' => 'required',
'password' => 'required'
)
);
if ($validator->fails()) {
// Redirect
} else {
$auth = Auth::attempt(array(
'Username' => Input::get('username'),
'Password' => Input::get('password'),
'Active' => 1
));
if ($auth) {
return Redirect::intended('dashboard');
} else {
return Redirect::route('login')
->with('global', 'Username/Password wrong, or account not activated');
}
}
// Redirect
}
And here goes my error, it always say Username/Password wrong, or account not activated.
Any ideas on this one?
I think its best to rename all your columns 'snake_case' style. A) this will work nicely with Laravel and B) it's good practice to keep all your database table names and columns etc the same.

Resources