When inserting models into the database I'd like to add 5 minutes to the timestamp property after each single model insert.
I thought that using Sequence like this would do the trick but it's not working: all models that are inserted still have the same timestamp.
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Factories\Sequence;
class TestSeeder extends Seeder
{
public function run()
{
\App\Models\Test::factory(1000)
->state(new Sequence(
fn () => ['timestamp' => now()->addMinutes(5)->toDateTimeString()],
))
->create();
}
}
I will suggest this approach maybe with the need for some adjustments:
class TestSeeder extends Seeder
{
private $testData = [];
public function run()
{
$now = now();
for ($i=0; $i < 100; $i++) {
$testData[] = [
'key1' => Str::random(10),
'key2' => Str::random(10),
'timestamp' => $now->addMinutes(5)->toDateTimeString()
];
}
foreach ($testData as $test) {
\App\Models\Test::create($test);
}
}
}
Use Factory Callbacks;
class TestFactory extends Factory
{
protected $model = Test::class;
/**
* Configure the model factory.
*
* #return $this
*/
public function configure()
{
return $this->afterCreating(function (Test $test) {
$test->update(['created_at' => Test::last()->created_at->addMinutes(5)]);
});
}
}
Seeder Class
public function run()
{
\App\Models\Test::factory(1000)->create();
}
You can use Eloquent Model Events: https://laravel.com/docs/8.x/eloquent#events
Ex:
class User extends Model
{
/**
* The "booted" method of the model.
*
* #return void
*/
protected static function booted()
{
static::created(function ($user) {
// update the time or any fields else
});
}
}
Related
Here is a service that implements interface
interface Rating
{
function create(User $user, User $rateduser, Rating $rating, Order $order);
}
The controller acceepts the request object:
{"rating": [{"rateid": 1, "rate": 4}], "userid": 1}
When I try to pass whole data into service:
public function store(RateRequest $request)
{
$rating = RatingData::from(
[
'rateid' => $request->get('rating.rateid'),
'rate' => $request->get('rating.rate'),
]
);
$user = Auth()::user();
$rateduser = User::find($request->id);
$order = new Order;
$this->rating->create($user, $rateduser, $rating, $order);
}
How is better pass parametres into funciton using ideniticators $userid, $rateUserId, $orderId or concrete models? Or left it such as now?
interface Rating
{
function create(int $userid, int $rateUserId, Rating $rating, int $order);
}
The controller looks dirty in my case.
Another problem what if tomorrow my model will be changed from eloquent to another? Then I have to change service methods
I can suggest the following approach if you want to keep your controllers clean. (I do not claim that it is the only correct one)
Dto:
class RatingDto extends DataTransferObject
{
public int $rateId;
public int $rate;
}
class CreateRatingDto extends DataTransferObject
{
public int $userId;
public int $rateUserId;
/** #var RatingDto[] */
#[CastWith(ArrayCaster::class, itemType: RatingDto::class)]
public array $ratings;
}
Request:
class CreateRatingRequest extends FormRequest
{
public function rules(): array
{
return [
'ratedUserId' => 'required|integer|exists:users,id',
'ratings' => 'required|array',
'ratings.*.rateId' => 'required|integer',
'ratings.*.rate' => 'required|integer|min:1|max:5',
];
}
public function getDto(): CreateRatingDto
{
return new CreateRatingDto([
'userId' => Auth::user()->id,
'ratedUserId' => $this->input('ratedUserId'),
'ratings' => $this->input('ratings'),
]);
}
}
Interface:
interface RatingServiceInterfate
{
public function create(CreateRatingDto $dto): RatingModel;
}
Controller:
class RatingController extends Controller
{
private RatingServiceInterfate $ratingService;
public function __construct(RatingServiceInterfate $ratingService)
{
$this->ratingService = $ratingService;
}
public function store(CreateRatingRequest $request): JsonResource
{
$rating = $this->ratingService->create($request->getDto());
return new RatingResource($rating);
}
}
Also, this implementation does not bind your service to models. At any time you can write a new interface implementation and substitute it through ServiceProvider. (if for example in another part of the application you need to save data to another database not compatible with eloquent)
I have problem creating a seeding hierarchy in Laravel, is there way i can achieve hierarchy by just creating a seeding data. I have 3 column [Vehicle,Model,Variant] so each vehicle has different model and variant. I will show you the sample table that I created on excel that I wanted to achieve.
Expected Output:
Here is my seeding function:
<?php
use Illuminate\Database\Seeder;
use App\Variant;
use App\Vehicle;
use App\VehicleModel;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class CarsSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
foreach (Vehicle::all() as $vehicle_data) {
foreach (VehicleModel::all() as $vehicle_model_data) {
foreach (Variant::all() as $variants_data) {
DB::table('cars')->insert([
[
'make' => $vehicle_data->name,
'model_id' => $vehicle_model_data->id,
'variant_id' => $variants_data->id,
'created_at'=>date('Y-m-d H:i:s'),
'updated_at'=>date('Y-m-d H:i:s')
]
]);
}
}
}
}
}
Car Model:
class Car extends Model
{
//
protected $table = 'cars';
protected $fillable = [
'id',
'make',
'variant_id',
'model_id'
];
public function variants() {
return $this->hasMany('App\Variant','id','variant_id');
}
public function models() {
return $this->hasMany('App\VehicleModel','id','model_id');
}
}
Variant Model:
class Variant extends Model
{
//
protected $table = 'variants';
protected $fillable = [
'name'
];
}
Vehicle Model:
class Vehicle extends Model
{
//
protected $table = 'vehicles';
protected $fillable = [
'name'
];
}
Vehicle Model:
class VehicleModel extends Model
{
//
protected $table = 'vehicle_models';
protected $fillable = [
'name'
];
}
Assuming the following relations
Vehicle hasMany VehicleModel(s)
VehicleModel hasMany Variant(s)
class Vehicle extends Model
{
public function vehicle_models()
{
return $this->hasMany(VehicleModel::class);
}
}
class VehicleModel extends Model
{
public function variants()
{
return $this->hasMany(Variant::class);
}
}
Try this
public function run()
{
$vehicles = Vehicle::with('vehicle_models.variants')->get();
foreach($vehicles as $vehicle) {
foreach($vehicle->vehicle_models as $model) {
foreach($model->variants as $variant){
Car::create([
'make' => $vehicle->name,
'model_id' => $model->id,
'variant_id' => $variant->id
]);
}
}
}
}
I need your help.
I'm working with Laravel Framework and I have a trouble with a belongsTo relationship.
My project have to tables, address book and delivery types, the columns in tables are:
address_book
id
name
mail
delivery_1
deliverytype_id_1
delivery_2
deliverytype_id_2
...
delivery_types
id
name
...
The code of delivery types model is this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class DeliveryType extends Model
{
protected $table = 'delivery_types';
protected $guarded = ['id'];
}
This is the address book model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class AddressBook extends Model
{
protected $table = 'address_book'; // table
protected $guarded = ['id']; // primary key
protected $appends = ['delivery', 'actions']; // accessors
protected $delivery = '';
public function del1() {
return $this->belongsTo('App\DeliveryType', 'deliverytype_id_1', 'id')->withDefault();
}
public function del2() {
return $this->belongsTo('App\DeliveryType', 'deliverytype_id_2', 'id');
}
/**
* Accessor: get the actions column information.
*
* #return string
*/
public function getActionsAttribute() {
$actions = '<a href='. route('admin.addressbook.show', $this->id) .'>'.
'show<i class="livicon" data-name="info" data-size="18" data-loop="true" data-c="#428BCA" data-hc="#428BCA" title="view contact"></i></a>';
return $actions;
}
/**
* Accessor: get the deliveries information.
*
* #return string
*/
public function getDeliveryAttribute () {
$deliveries = [
['val' => $this->delivery_1, 'type' => $this->del1()->name], //row error
['val' => $this->delivery_2, 'type' => $this->del2()->name]
];
foreach($deliveries as $delivery) {
$this->delivery = (strlen($delivery['val']) > 0) ?
$this->appendString($this->delivery, '<strong>'.$delivery['type'].'</strong> '.$delivery['val']) :
$this->delivery;
}
return $this->delivery;
}
protected function appendString(string $str, string $val) {
return (strlen($str) > 0) ? $str.'<br>'.$val : $val;
}
In the html page the data is loaded through ajax call to the controller function. This is the code of function:
public function data(Request $request) {
// Init data
$this->addressbooks = AddressBook::get(
['address_book.id',
'address_book.name',
'address_book.email',
'address_book.delivery_1',
'address_book.delivery_2');
// Return json array
header("Content-Type: application/json");
return $this->addressbooks;
}
When the page call the function through ajax, the framework return the error "Undefined property" in the accessor getDeliveryAttribute, where I try to call relationship belongs to about delivery type ID and its reference in the delivery types table.
What I'm doing wrong? Thanks in advance to those who can help me.
Here is how I would write the AddressBook model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
//use Illuminate\Support\Facades\Log;
class AddressBook extends Model
{
protected $table = 'address_book'; // table
//protected $guarded = ['id']; // primary key
// always load these accessors
protected $appends = [
'delivery',
'actions',
];
protected $mail = '';
// No need for $delInfo, use $this->delivery (same data)
// protected $delInfo = '';
public function category() {
/*
You can use `AddressBookCategory::class` instead of `'App\DeliveryType'`
http://php.net/manual/en/migration55.new-features.php#migration55.new-features.class-name
Because models are in the same napespace, we don't need to write \App\AddressBookCategory
*/
return $this->belongsTo(AddressBookCategory::class, 'address_book_category_id', 'id');
}
public function deliveryType1() {
return $this->belongsTo(DeliveryType::class, 'deliverytype_id_1', 'id')
/*
Specify empty string for the name, so that when we access
$this->deliveryType1->name it's equal ''
*/
->withDefault(['name' => '']);
}
public function deliveryType2() {
return $this->belongsTo(DeliveryType::class, 'deliverytype_id_2', 'id')
->withDefault(['name' => '']);
}
/**
* Accessor: get the actions column information.
*
* Access by using: $this->action
*
* #return string
*/
public function getActionsAttribute() {
// moved this into multi line, easier to read
return '<a href='. route('admin.addressbook.show', $this->id) .'>'
.'show'
.'<i class="livicon" data-name="info" data-size="18" data-loop="true" data-c="#428BCA" data-hc="#428BCA" title="view contact"></i>'
.'</a>';
}
/**
* Accessor: get the deliveries information
*
* Access by using: $this->delivery
*
* #return string
*/
public function getDeliveryAttribute () {
// I've updated logic here, it should be easier to read...
$delivery = [];
if ( ! empty($this->delivery_1) ) {
$delivery[] = '<strong>'.$this->deliveryType1->name.'</strong> '.$this->delivery_1;
}
if ( ! empty($this->delivery_2) ) {
$delivery[] = '<strong>'.$this->deliveryType2->name.'</strong> '.$this->delivery_2;
}
// join array elements with a string `<br>`
return implode('<br>', $delivery);
}
}
In several of my models I have code like this
public function setTotalAttribute($value)
{
return $this->attributes['total'] = $value * 100;
}
public function getTotalAttribute($value)
{
return $value * 0.01;
}
Sometimes the field that I am mutating is called purchase or price, but the code is the same (changing 7.99 to 799 to store in the DB, and change it back on return).
If all the fields were named the same I could use a trait, however they are slightly different.... is there a way I can setup something similar to the date fields which auto-mutate to Carbon instances?
One solution is to define the fields that deal with dollars/cents conversion in the models that have such fields, and then use a trait to override the global mutators/accessors.
class Model
{
use HasMoneyFields;
protected $moneyFields = ['purchase', 'price', 'total'];
}
trait HasMoneyFields
{
public function getAttributeValue($key)
{
$value = parent::getAttributeValue($key);
if (property_exists($this, 'moneyFields')) {
if (in_array($key, $this->moneyFields)) {
$value /= 100;
}
}
return $value;
}
public function setAttribute($key, $value)
{
parent::setAttribute($key, $value);
if (property_exists($this, 'moneyFields')) {
if (in_array($key, $this->moneyFields)) {
$this->attributes[$key] = $value * 100;
}
}
}
}
You might be interested in https://github.com/topclaudy/eloquent-mutators
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use \Awobaz\Mutator\Mutable;
protected $accessors = [
'title' => 'trim_whitespace',
'content' => 'trim_whitespace',
];
}
The package allows you to create custom accessors/mutators extensions.
my codes are constructed by Laravel+dingo.
I have two models which are one to many relationship:
Reservation.php (Master)
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Reservation extends Model
{
protected $table = 'dbo.Reservation';
public function hasManyReservationDetails()
{
return $this->hasMany('App\Models\ReservationDetail', 'ReservationID', 'ReservationID');
}
}
ReservationDetail.php (Detail)
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ReservationDetail extends Model
{
protected $table = 'dbo.ReservationDetail';
public function belongsToReservation()
{
return $this->belongsTo('App\Models\Reservation', 'ReservationID', 'ReservationDetailID');
}
}
And two Transformers for the two models as following:
ReservationTransformer
public function transform(Reservation $reservation)
{
return [
'reservation_id' => (int) $reservation->ReservationID,
'reservation_no' => $reservation->ReservationNo,
];
}
ReservationDetail Transformer
public function transform(ReservationDetail $reservation_detail)
{
return [
'reservation_detail_id' => (int) $reservation_detail->ReservationDetailID,
'reservation_id' => (int) $reservation_detail->ReservationID,
'room_no' => $reservation_detail->RoomNo,
];
}
My controller and inquire
$reservation = Reservation::where('ReservationNo', '=', $reservation_no)
->with('ReservationDetails')
->get();
return $reservation;
I get the following return
{
"Reservations": [
{
"ReservationID": "1",
"ReservationNo": "2016-06-01 16:50:59.0659",
"reservation_details": [
{
"ReservationDetailID": "1",
"ReservationID": "1",
"RoomNo": "001",
},
{
"ReservationDetailID": "2",
"ReservationID": "1",
"RoomNo": "002",
}
]
}
]
}
I try the following but only return the translation of master table.
$reservation = $this->collection($reservation, new ReservationTransformer());
**
How can I transform the the data of master and detail table together?
**
I am not really understand how 'Custom Transformation Layer' works, anyone who can give me an example?
Many Thanks.
You can use fractals build in support for transformer includes. See http://fractal.thephpleague.com/transformers/
I would start by renaming public function hasManyReservationDetails() to public function reservationDetails().
And then your ReservationTransformer will take care of the rest:
use League\Fractal\TransformerAbstract;
class ReservationTransformer extends TransformerAbstract
{
/**
* List of resources to automatically include
*
* #var array
*/
protected $defaultIncludes = [
'reservationDetails',
];
public function transform(Reservation $reservation)
{
return [
'reservation_id' => (int) $reservation->ReservationID,
'reservation_no' => $reservation->ReservationNo,
];
}
/**
* Include ReservationDetail
*
* #param Reservation $reservation
*
* #return League\Fractal\Resource\Collection
*/
public function includeReservationDetails(Reservation $reservation)
{
return $this->collection($reservation->reservationDetails, new ReservationDetailTransformer);
}
}