How to save entities across two databases in Laravel (Backpack) - php

I am new with Laravel and of course also with backpack.
I am using laravel 7.x and backpack 4.1 with both MySql and MongoDb.
The situation I am facing is that I have a company model with some attributes which are in MySql (both save and update working great with for the attributes stored in MySql) and other attributes that should be stored in MongoDb.
I have a CompanyPropertyCollection model for the attributes which I want to be stored in MongoDb
All these company will have a variable number of other arbitrary properties, which I want to save in mongo.
These properties may be simple scalar values or more complex values too (think arrays of objects), hence the idea to save them in mongo.
MySql Company table:
My question is the following:
What is the best practice to save attributes of an entity in two distinct databases from BackPack? I override the CreateOperation, UpdateOperation with the store() and update() functions something like this:
Company model:
class Company extends Model
{
use \Backpack\CRUD\app\Models\Traits\CrudTrait;
use SoftDeletes;
use HybridRelations;
protected $connection = 'mysql';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name',
'company_type',
'is_active',
'package_id',
'certification_id',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'id' => 'integer',
'is_active' => 'boolean',
'package_id' => 'integer',
'certification_id' => 'integer',
];
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function users()
{
return $this->belongsToMany(\App\Models\User::class);
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function package()
{
return $this->belongsTo(\App\Models\Package::class);
}
public function certification()
{
return $this->hasMany(\App\Models\Certification::class);
}
public function properties()
{
return $this->hasOne(\App\Models\CompanyPropertyCollection::class);
}
}
CompanyPropertyCollection model:
class CompanyPropertyCollection extends Model
{
use SoftDeletes;
protected $connection = 'mongodb';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
//'company_id',
'email',
'big_news_id',
'phone.number',
'phone.country_prefix',
'phone.area_prefix',
'phone.postfix',
'year_of_foundation',
'nr_of_employees',
'nr_of_branches',
'company_size',
'subtitle',
'homepage',
'country_code',
'city',
'street',
'post_code',
'uid_nr',
'registration_nr',
'total_sales_area',
'total_annual_bisuness_volume',
'short_portrait',
'long_portrait',
'embedded_video',
'certificates',
'gallery',
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'id' => 'integer',
'gallery' => 'array',
];
public function company()
{
return $this->belongsTo(\App\Models\Company::class);
}
}
CompanyCrudController :
public function update()
{
//$this->update( $company->properties);
$response = $this->traitUpdate();
// do something after save
//use registered observer
return $response;
}
Currently I am trying to use a CompanyObserver and on saving to store the data for mongo from the Request.
class CompanyObserver {
public function saving(Company $company)
{
//dd(request()->request);
$request = request()->request;
dd($company->properties());
//save to MongoDb
dd('saving methond on the observer');
}
}

If you need to perform some action after saving a model, like saving some user data in another db with a different type. You can override the model's save method.
Inside your model, add a method like the below
public function save(array $options = array())
{
if (parent::save($options)) {
// Model has been saved in mysql, now save in mongoDB
}
}

Related

Better solution to get column from Relationship with Main Model columns

Need a better solution
There is a Post which belongs to multiple Categories and both having Many-to-Many relationship in between them. The intermediate table for many-to-many relationship is PostCategory. PostCategory contains post_id, category_id and sequence of the post. I want to get this sequence with the Post model attributes (title, description, ...).
To get this, am doing like this
$posts = Post::where([
'is_active' => 1,
'is_deleted' => 0,
'is_published' => 1,
'status' => 'publish'
])->whereHas('category', function ($query) use ($params) {
return $query->where([
'category_id' => $params['categoryId'],
]);
})->with([
'category' => function ($query) use ($params) {
return $query->where([
'category_id' => $params['categoryId'],
]);
}
])
->orderBy('live_date', 'DESC')
->orderBy('publish_time', 'DESC')
->get()
->toArray();
foreach ($posts as &$post) {
$post['sequence'] = $post['category']['sequence'];
}
Am getting the expected result but as you can see, first I've to use the closure twice and then have to iterate through entire collection to set sequence at the top-level but as I mentioned, I need a better solution to this (If any)
Post.php
namespace App\Models\Mongo;
/**
* #mixin \Illuminate\Database\Eloquent\Builder
* #mixin \Jenssegers\Mongodb\Query\Builder
*/
class POST extends \Jenssegers\Mongodb\Eloquent\Model
{
/** #var string Mongo Connection Name */
//protected $connection = 'mongodb';
/** #var string Mongo Collection Name */
protected $collection = 'posts';
/** #var bool Enable/Disable Timestamp */
public $timestamps = true;
/** #var string Date format */
protected $dateFormat = 'Y-m-d H:i:s';
/** #var array */
protected $dates = ['created_at', 'updated_at', 'live_date', 'expire_date'];
/**
* // I know this relation is not correct, it must either belongsToMany or hasMany
* // but as of now, I've to fetch the posts belonging to a single category id
* // so using hasOne relation
* #return \Jenssegers\Mongodb\Relations\HasOne
*/
public function category()
{
return $this->hasOne(
PostCategory::class,
'post_id',
'_id'
);
}
}
PostCategory.php
namespace App\Models\Mongo;
/**
* #mixin \Illuminate\Database\Eloquent\Builder
* #mixin \Jenssegers\Mongodb\Query\Builder
*/
class PostCategory extends \Jenssegers\Mongodb\Eloquent\Model
{
/** #var string Mongo Connection Name */
//protected $connection = 'mongodb';
/** #var string Mongo Collection Name */
protected $collection = 'post_category';
/**
* #return \Jenssegers\Mongodb\Relations\HasMany
*/
public function post()
{
return $this->hasMany(Post::class, '_id', 'post_id');
}
}
Changes
change relation to belongsToMany in Post
Relation is not working
return $this->belongsToMany(
Category::class,
'post_category',
'post_id',
'category_id',
'_id', <-- post primary key
'_id', <-- category primary key
)->withPivot('sequence');
You could use a many-to-many relationship instead and access sequence as pivot column.
namespace App\Models\Mongo;
/**
* #mixin \Illuminate\Database\Eloquent\Builder
* #mixin \Jenssegers\Mongodb\Query\Builder
*/
class POST extends \Jenssegers\Mongodb\Eloquent\Model
{
/** #var string Mongo Connection Name */
//protected $connection = 'mongodb';
/** #var string Mongo Collection Name */
protected $collection = 'posts';
/** #var bool Enable/Disable Timestamp */
public $timestamps = true;
/** #var string Date format */
protected $dateFormat = 'Y-m-d H:i:s';
/** #var array */
protected $dates = ['created_at', 'updated_at', 'live_date', 'expire_date'];
/**
* // I know this relation is not correct, it must either belongsToMany or hasMany
* // but as of now, I've to fetch the posts belonging to a single category id
* // so using hasOne relation
* #return \Jenssegers\Mongodb\Relations\HasOne
*/
public function category()
{
return $this->belongsToMany(
Category::class
)->withPivot('sequence');
}
}
You probably have to add one or more optional parameters to belongsToMany() to make it work. But since you know your data structure better than I do, I bet, you can figure that out faster than I can.

How to delete data using Eloquent query?

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.

Laravel Lighthouse GraphQL renamed relationship mutation

I'm having an issue updating a renamed relationship with a graphQL query.
Here's the related schema and Laravel models:
Laravel Models
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Lead extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
// protected $fillable = [
// 'lead_type_id',
// ];
protected $guarded = [];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
//
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
// 'created_at' => 'timestamp',
// 'updated_at' => 'timestamp'
];
/**
* Get the LeadActions for the Lead.
*/
public function leadActions()
{
return $this->hasMany(\App\Models\LeadAction::class);
}
/**
* Get the clients for the Lead.
*/
public function clients(): BelongsToMany
{
return $this->belongsToMany(Client::class);
}
/**
* Get the LeadType for the Lead.
*/
public function leadType(): BelongsTo
{
return $this->belongsTo(\App\Models\LeadType::class, 'lead_type_id');
}
}
?>
<?php
namespace App\Models;
use App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class LeadType extends Model
{
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
//
];
/**
* The attributes that should be cast to native types.
*
* #var array
*/
protected $casts = [
'name' => 'string',
'created_at' => 'timestamp',
'updated_at' => 'timestamp'
];
/**
* Get the Leads for the LeadType.
*/
public function leads(): HasMany
{
return $this->hasMany(Models\Lead::class);
}
}
?>
GraphQl Schema
type Lead {
id: ID!
lead_type: LeadType #belongsTo(relation: "leadType")
clients: [Client!]! #belongsToMany
created_at: DateTime!
updated_at: DateTime!
}
input UpdateLeadInput {
id: ID!
clients: UpdateClientsRelation
lead_type: UpdateLeadTypeRelation
}
input UpdateLeadTypeRelation {
create: CreateLeadTypeInput
connect: ID
update: UpdateLeadTypeInput
upsert: UpsertLeadTypeInput
delete: ID
disconnect: ID
}
Using the following graphQl query I get an SQL error for missing column lead_type:
Query
mutation UpdateLead {
updateLead(input: {id: 1, lead_type: {connect: 1}}) {
id
clients {
id
name
}
lead_type {
id
name
}
}
}
SQL Error
Column not found: 1054 Unknown column 'lead_type' in 'field list' (SQL: update `leads` set `lead_type` = {\"connect\":\"1\"}, `leads`.`updated_at` = 2020-01-14 17:11:17 where `id` = 1
I've followed the Laravel naming convention, and named the column lead_type_id on the leads table. If I remove the renaming of the lead_type relationship to leadType I can successfully run an update mutation, but I can't figure out how to get it to use the correct name for the column (lead_type_id) whilst keeping the relationship renamed.
Any help is greatly appreciated.
Many Thanks
Have you tried #rename directive? I mean you have to use it on lead_type in your UpdateLeadInput, because lighthouse looks for relation named lead_type, and as this is not defined, it assume that lead_type is an argument.
Either rename your relations in models, like:
class Lead extends Model
{
public function lead_actions()
{
return $this->hasMany(\App\Models\LeadAction::class);
}
public function lead_type(): BelongsTo
{
return $this->belongsTo(\App\Models\LeadType::class, 'lead_type_id');
}
}
OR
use #rename directive (I didn't try it, but I mean it works like this):
input UpdateLeadInput {
id: ID!
clients: UpdateClientsRelation
lead_type: UpdateLeadTypeRelation #rename(attribute: "leadType")
}

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'projects_id' in 'field list'

I am trying to submit some data to database. However when inserting the $airattributes im getting an error that projects_id (WHICH DOESNT EXIST ANYWHERE, NEITHER IN DB NOR ANY FILE)
MY CONTROLLER:
public function newProject(Request $request)
{
$data = $request->all();
$attributes = [];
$attributes['title'] = $data['title'];
$attributes['start_date'] = date("Y-m-d h:i:s", strtotime($data['start_date']));
$attributes['end_date'] = date("Y-m-d h:i:s", strtotime($data['end_date']));
$attributes['created_by'] = Auth::user()->id;
$attributes['description'] = $data['description'];
$attributes['air'] = '10';
$attributes['water'] = '19';
$attributes['lat'] = $data['lat'];
$attributes['lng'] = $data['lng'];
$airattributes['dust'] = $data['dust'];
$airattributes['noise'] = $data['noise'];
$airattributes['temperature'] = $data['temperature'];
$airattributes['radiation'] = $data['radiation'];
// var_dump($attributes);
// return;
$project = Projects::create($attributes);
$air = $project->air()->create($airattributes);
var_dump($data);
return;
MY projects model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Projects extends Model
{
protected $table = 'projects';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'title', 'start_date', 'end_date', 'created_by', 'description', 'air', 'water', 'lat', 'lng'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'id',
];
/**
* Get the phone record associated with the user.
*/
public function air()
{
return $this->hasMany('App\Air');
}
/**
* Get the phone record associated with the user.
*/
public function water()
{
return $this->hasOne('App\Water');
}
public function user()
{
return $this->hasOne('\App\User', 'id', 'created_by');
}
public function enrolls()
{
return $this->hasMany('\App\Enroll', 'project_id', 'id');
}
public function lastEdited()
{
return $this->hasOne('\App\User', 'id', 'last_edited_by');
}
}
My Air Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Air extends Model
{
protected $table = 'projects_air';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'project_id', 'temperature', 'radiation', 'dust', 'noise'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'id',
];
}
you can test it here for more info: http://188.166.166.143/projects/add
In the projects model if you don't specify the foreign key, then I believe in the Air model you should change to 'projects_id' in accordance to the name of the table.
Projects hasMany relationships with Air:
public function air()
{
return $this->hasMany('App\Air');
}
This, by default assumes that air model have foreign key projects_id, hence the error.
Since, you have project_id foreign key,
return $this->hasMany('App\Air', 'project_id');
will do.
Simply: Changing model name Projects to Project will solve the problem. Also model names are always singular.
You can solve this in two way. One you can change you model name to Project or you can specify you foreign key in your air function. So you can Change you air function to below.
public function air()
{
return $this->hasMany('App\Air', 'project_id');
}
Choose whichever you want. But in Laravel Model name is always singular so I will recommend you to follow the first rule in that way you don't need to specify the foreign key Laravel is smart enough to recognize that automatically.

Laravel update database table design

I have a php laravel projekt where I need to add a field to one/more models (Eloquent). I don't have much experience in php and never tried laravel before.
The class looks like this now
class Player extends Eloquent
{
use GenderTrait;
use VisibilityTrait;
use PlayerPhotoTrait;
use PlayerActionTrait;
const GENDER_MALE = 2;
const GENDER_FEMALE = 1;
/**
* The database table used by model.
*
* #var string
*/
protected $table = 'players';
/**
* Parameters for `actions` relation.
*
* #see PlayerActionTrait::actions()
* #var array
*/
protected $actionModel = [
'name' => 'PlayerAction',
'foreignKey' => 'player_id',
];
/**
* The list of mass-assignable attributes.
*
* #var array
*/
protected $fillable = [
'name',
'start_value',
'gender',
'is_visible',
'nation',
];
/**
* The list of validation rules.
*
* #var array
*/
public static $rules = [
'name' => 'required',
'nation' => 'required|numeric',
'start_value' => 'required|numeric',
];
/**
* #inheritdoc
*/
protected static function boot()
{
parent::boot();
}
/**
* Players country.
*
* #return Country
*/
public function country()
{
return $this->belongsTo('Country', 'nation');
}
/**
* Player videos.
*
* #return mixed
*/
public function videos()
{
return $this->morphMany('YoutubeLink', 'owner');
}
}
I would like to add a string field called "level" but I have no idea how to go about it. If I create the field in MySQL first and then the models get updated, if I update the models and then Laravel update MySQL for me?
Im looking forward to hearing what I can do :)
You need to add migration:
php artisan make:migration add_fields_to_players_table --table=players
Open in /database/migrations new migration and write
Schema::table('players', function ($table) {
$table->string('new_string_field');
});
Now you need to run migrations
php artisan migrate
More info and available column types here

Categories