Laravel - elastic search with Laravel Scout package softdelete is not working - elasticsearch

I have implemented elasticsearch in laravel 5.5 using "babenkoivan/scout-elasticsearch-driver" package.
composer.json
"require": {
"babenkoivan/scout-elasticsearch-driver": "^2.2",
"elasticsearch/elasticsearch": "^5.1",
"laravel/scout": "^3.0"
}
GGIndexConfigurator file use to creat elastic search index.
<?php
namespace App;
use ScoutElastic\IndexConfigurator;
use ScoutElastic\Migratable;
class GGIndexConfigurator extends IndexConfigurator
{
use Migratable;
protected $settings = [
//
];
protected $defaultMapping = [
'properties' => [
'id' => [
'type' => 'integer',
],
'title' => [
'type' => 'string',
],
'img' => [
'type' => 'string',
],
'url' => [
'type' => 'string',
],
'type' => [
'type' => 'string',
],
'location' => [
'type' => 'geo_point',
],
'created_at' => [
'type' => 'date',
'format' => 'yyyy-MM-dd HH:mm:ss'
]
]
];
}
MySearchRule file tell elastic to search on title field.
<?php
namespace App;
use ScoutElastic\SearchRule;
class MySearchRule extends SearchRule
{
public function buildQueryPayload()
{
return [
'must' => [
'match' => [
'title' => $this->builder->query
]
]
];
}
}
MegaBrand file to store / update / remove record in elastic search in same pattern like add id , title , url , img etc..
<?php
namespace App;
use Carbon\Carbon;
use ScoutElastic\Searchable;
use Illuminate\Database\Eloquent\Model;
class MegaBrand extends Model
{
use Searchable;
protected $table = 'brands';
protected $primaryKey = 'brand_id';
protected $indexConfigurator = GlobalGarnerIndexConfigurator::class;
protected $searchRules = [
MySearchRule::class
];
protected $mapping = [
//
];
/**
* Get the index name for the model.
*
* #return string
*/
public function searchableAs()
{
return 'brands';
}
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
//$array = $this->toArray();
return [
'id' => $this->brand_id,
'title' => $this->brand_name,
'img' => $this->image,
'url' => $this->website,
'type' => 'brands',
'location' => [],
'created_at' => Carbon::now()->toDateTimeString()
];
}
}
above all code is for elastic search files and configurations. Now I run commend to create elastic search index.
php artisan elastic:create-index App\GGIndexConfigurator
index is create successfully with.
BrandModel file
<?php
namespace App\Model;
use App\GlobalGarnerIndexConfigurator;
use app\MySearchRule;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use ScoutElastic\Searchable;
class GiftIndiaModel extends Model
{
use SoftDeletes;
use Searchable;
protected $table = 'brands';
protected $primaryKey = 'brand_id';
protected $fillable = ['hash', 'brand_name', 'category', 'description', 'terms', 'image', 'discount', 'tat', 'isOnline', 'website', 'status', 'gg_brand_status'];
protected $indexConfigurator = GlobalGarnerIndexConfigurator::class;
protected $searchRules = [
MySearchRule::class
];
protected $mapping = [
//
];
/**
* Get the index name for the model.
*
* #return string
*/
public function searchableAs()
{
return 'brands';
}
/**
* Get the indexable data array for the model.
*
* #return array
*/
public function toSearchableArray()
{
//$array = $this->toArray();
return [
'id' => $this->brand_id,
'title' => $this->brand_name,
'img' => $this->image,
'url' => $this->website,
'type' => 'brand',
'location' => [],
'created_at' => Carbon::now()->toDateTimeString()
];
}
Upto this point all is good not have any issue. now I create new function to add brand in database using eloquent and that brand is also added in elastic search :)
public function addBrand(){
$brand = new GiftIndiaModel([
'hash' => 'qwertyy123',
'brand_name' => 'microsoft',
'category' => 'laptop',
'description' => 'its good brand',
'terms' => 'lorum ipsum',
'image' => 'www.dell.com/image',
'discount' => 5,
'tat' => 2,
'isOnline' => 'yes',
'website' => 'www.dell.com',
'status' => 1
]);
if($brand->save()){
return response()->json(true , 200);
}
return response()->json(false , 400);
}
So addBrand function is working fine and I have tried php artisan scout:import App\\Brand command to add brand in elastic search with all success.
Real issue is when I softdelete this brand it doesn't affect elastic search index and my softdelete brand is still available in elastic search.
Update Brand function
public function updateBrand(){
return GiftIndiaModel::where('brand_id', 3)->delete();
}
When I run this function brand is successfully soft deleted from table and deleted_at field store detele date time.But it's not reflecting to elastic index.
Any advice ?

As #Babenko tell me in mail that soft deleting is not supported right now.
I find other way to do this.
public function updateBrand()
{
$brand = GiftIndiaModel::where('brand_id', 3)->first();
$brand->deleted_at = date("Y-m-d");
$brand->save();
$brand->unsearchable();
}
In above code you can see I have used unsearchable() method to remove item from elastic search index after soft delete.
I have also create issue/feature request in github.
github
If anyone having issue while updating / inserting record in elastic search I recommend you to use save() method of model.

Related

Laravel Resource return value from another Resource?

I tried to find a solution here but nothing worked. I want to return values from TagResource using MealResource because I have TagTranslations table and I'm getting the data from the table with translations in TagResource.
Relationships are correctly formed, meal and tag models are connected via meal_tags table and tagtranslations belongsTo Tag::class.
I used TagResource like this:
class TagResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
$translation = $this->translations->where('tag_id', $this->id)->first();
return
[
'id' => $this->id,
'title' => $translation->title,
'slug' => $translation->slug,
];
}
}
and MealResource like this:
public function toArray($request)
{
$translation = $this->translations->where('meal_id', $this->id)->first();
$category_translation = $this->category->translations->where('category_id', $this->category->id)->first();
return [
'id' => $this->id,
'title' => $translation->title,
'decription' => $translation->description,
'category' => [
'id' => $this->category->id,
'title' => $category_translation->title,
'slug' => $category_translation->slug,
],
'tags' => FILL IN THE BLANK (have used TagResource:collection() and new TagResource()) and didnt work
];
}
public function toArray($request)
{
$translation = $this->translations->where('meal_id', $this->id)->first();
$category_translation = $this->category->translations->where('category_id', $this->category->id)->first();
return [
'id' => $this->id,
'title' => $translation->title,
'decription' => $translation->description,
'category' => [
'id' => $this->category->id,
'title' => $category_translation->title,
'slug' => $category_translation->slug,
],
'tags' => TagResource::collection($this->tags),
];
}
If all the Relationships namings/mappings are correct then this will work.And please make sure that model are perfectly mapped respectively.

how to update key/value database with laravel?

I'm just learning laravel. I want update key / value in database with laravel api but not work.
My products model is one to many with ProductMeta and many to many with contents model.
My Models
class Product extends Model
{
use HasFactory;
protected $guarded = [];
public function productMeta()
{
return $this->hasMany(ProductMeta::class);
}
public function content()
{
return $this->belongsToMany(Content::class, 'product_contents')->withTimestamps();
}
}
class ProductMeta extends Model
{
use HasFactory;
protected $guarded = [];
public function products()
{
return $this->belongsTo(Product::class);
}
}
class Content extends Model
{
use HasFactory;
protected $guarded= [];
public function product()
{
return $this->belongsToMany(Product::class, 'product_contents');
}
Controller
public function update(Request $request, $id)
{
$product = Product::findOrFail($id);
DB::table('product_metas')
->upsert(
[
[
'product_id' => $product->id,
'key' => 'name',
'value' => $request->name,
],
[
'product_id' => $product->id,
'key' => 'price',
'value' => $request->name,
],
[
'product_id' => $product->id,
'key' => 'amount',
'value' => $request->name,
],
],
['product_id','key'],
['value']
);
return \response()->json([], 204);
}
Table Structure
API parameter
I tried with update and updateOrcreate and updateOrInsert and upsert methods.
just in upsert method writed database but inserted new data.not updated.
In your case, you should use updateOrCreate() instead of upsert.
Product::updateOrCreate([
'product_id' => $id,
'name' => $request->name,
'price' => $request->price,
'amount' => $request->amount
]);
or
Product::upsert([
[
'product_id' => $id,
'name' => $request->name,
'price' => $request->price,
'amount' => $request->amount
]
], ['product_id'], ['name', 'price', 'amount']);
In addition your problem is your table name is not matching with your structure table name. In your controller DB::table('product_metas') should be DB::table('products_meta').
my problem solved this way:
ProductMeta::query()->where('product_id', $id)->upsert([
['product_id' => $id, 'key' => 'name', 'value' => $request->name],
['product_id' => $id, 'key' => 'price', 'value' => $request->price],
['product_id' => $id, 'key' => 'amount', 'value' => $request->amount]],
['product_id'], ['value']);
$contentRecord = Product::find($id);
$contentRecord->content()->update(['path'=>$request->path]);
return response()->json([], 204);
I forget use query() method for ProductMeta and added $table->unique(['product_id', 'key']); to product meta migration.
**products relation one to many with product Meta
And Many to many with content.

codeigniter 4 migration file not creating table

when I first made a migration file for table users, the public function down() in the migration file was empty. when I run php spark migrate the table users was created.
then I generated another migration file with php spark make:migration users, made a few adjustments according to the new table structure and put $this->forge->dropTable('users'); in the public function down(). but when I run php spark migrate again, the users table doesn't have the new field..
I'm using codeigniter 4 and mysql. here's my code
UserModelphp
<?php
namespace App\Models;
use CodeIgniter\Model;
class UserModel extends Model
{
protected $DBGroup = 'default';
protected $table = 'users';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $insertID = 0;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
// added created_at and updated_at
protected $allowedFields = ['username', 'password', 'foto', 'nama', 'email', 'telepon', 'created_at', 'updated_at'];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}
first migration file
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Users extends Migration
{
public function up()
{
// tabel users
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 7,
'auto_increment' => true,
],
'username' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => false,
],
'password' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false,
],
'profile_pic' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'nama' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'email' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'telepon' => [
'type' => 'VARCHAR',
'constraint' => 10,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('users');
}
public function down()
{
// hapus tabel users
}
}
new migration file
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class Users extends Migration
{
public function up()
{
// tabel users
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 7,
'auto_increment' => true,
],
'username' => [
'type' => 'VARCHAR',
'constraint' => 50,
'null' => false,
],
'password' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => false,
],
'foto' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'nama' => [
'type' => 'VARCHAR',
'constraint' => 50,
],
'email' => [
'type' => 'VARCHAR',
'constraint' => 100,
],
'telepon' => [
'type' => 'VARCHAR',
'constraint' => 10,
],
'created_at DATETIME DEFAULT CURRENT_TIMESTAMP',
'updated_at DATETIME DEFAULT CURRENT_TIMESTAMP',
]);
$this->forge->addKey('id', true);
$this->forge->createTable('users');
}
public function down()
{
// hapus tabel users
$this->forge->dropTable('users');
}
}
can someone tell me what I'm doing wrong? any help is appreciated
Explanation:
The down() method isn't called when you execute php spark migrate.
The down() method is run when you perform a migration rollback process using php spark migrate:rollback.
Solution:
Add the $this->forge->dropTable('users'); line of code at the beginning of the up() method of the "new migration file".
new migration file
// ...
class Users extends Migration
{
public function up()
{
$this->forge->dropTable('users');
// ...
}
// ....
}
The purpose of the down() method is to "reverse" everything performed in the up() method.
Extra Notes:
Considering that in your new migration, you're only renaming an existing table column (profile_pic -> foto) and adding timestamp columns, it would make more sense if you specified a more meaningful "migration name".
In addition, instead of dropping & recreating the existing table, modify the table instead.
I.e:
new migration file
A. Command (Create the new migration):
php spark make:migration alter_users_rename_profile_pic_add_timestamps
B. Generated migration.
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class AlterUsersRenameProfilePicAddTimestamps extends Migration
{
private $tableName = "users";
public function up()
{
$this->forge->modifyColumn($this->tableName, [
"profile_pic" => [
'name' => 'foto',
'type' => 'VARCHAR',
'constraint' => 50,
]
]);
$this->forge->addColumn($this->tableName, [
'created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP',
'updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
]);
}
public function down()
{
$this->forge->modifyColumn($this->tableName, [
"foto" => [
'name' => 'profile_pic',
'type' => 'VARCHAR',
'constraint' => 50,
]
]);
$this->forge->dropColumn($this->tableName, ["created_at", "updated_at"]);
}
}

Laravel-Auditing is not working without any errors

I've recently installed this package and configured everything with guide but some how it's not working!
By it's not working I mean it's not adding anything to database. I really don't know what is wrong with my configs but I've checked everything with guide 3 times and everything is correct but... I don't know
config/audit.php:
<?php
return [
'enabled' => env('AUDITING_ENABLED', true),
'implementation' => OwenIt\Auditing\Models\Audit::class,
'user' => [
'morph_prefix' => 'user',
'guards' => [
'web',
'api',
],
],
'resolver' => [
'user' => OwenIt\Auditing\Resolvers\UserResolver::class,
'ip_address' => OwenIt\Auditing\Resolvers\IpAddressResolver::class,
'user_agent' => OwenIt\Auditing\Resolvers\UserAgentResolver::class,
'url' => OwenIt\Auditing\Resolvers\UrlResolver::class,
],
'events' => [
'created',
'updated',
'deleted',
'restored',
'gold_mailed' => 'goldMailed',
'invited' => 'clientInvited',
],
'strict' => false,
'timestamps' => false,
'threshold' => 0,
'driver' => 'session',
'drivers' => [
'eloquent' => [
'table' => 'audits',
'connection' => null,
],
],
'console' => true,
];
My model that I want to audit:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use OwenIt\Auditing\Contracts\Auditable;
use App\Models\Expansion;
use App\Models\Audit;
class Setting extends Model implements Auditable
{
protected $table = 'settings';
use \OwenIt\Auditing\Auditable;
protected $fillable = [
'expansion_id', 'season', 'advertiser_app', 'pvp_app', 'raid_app', 'version'
];
protected $auditInclude = [
'expansion_id', 'season', 'advertiser_app', 'pvp_app', 'raid_app', 'version'
];
public function Expansion()
{
return $this->hasOne(Expansion::class, 'id', 'expansion_id');
}
}
web.php:
Route::post('/setting' , 'Admin\SuperAdminController#saveSetting')->middleware('superadmin')->name('admin_save_setting');
Controller:
public function saveSetting(Request $request)
{
$sql = Setting::where('id', 1)->update([
'expansion_id' => $request['expansion_id'],
'season' => $request['season'],
'advertiser_app' => $request['advertiser_app'],
'pvp_app' => $request['pvp_app'],
'raid_app' => $request['raid_app'],
'version' => $request['version']
]);
if ($sql) {
toastr()->success('Settings successfully updated.');
return redirect()->back();
}
toastr()->error('Something went wrong!');
return redirect()->back();
}
I don't know what infos do you need but I think this is enough
I think my problem is with "driver" in config file , I don't know if that's correct or not
[UPDATED]
Based on the controller code you showed, it didn't work because your code is being called using Builder style, and the package only works when it is called using Eloquent style.
Documentation link
So, maybe you need to change your code to:
$setting = Setting::where('id', 1)->firstOrFail();
$setting->update([
'expansion_id' => $request['expansion_id'],
'season' => $request['season'],
'advertiser_app' => $request['advertiser_app'],
'pvp_app' => $request['pvp_app'],
'raid_app' => $request['raid_app'],
'version' => $request['version']
]);
now I have another problem -_-
this is my controller:
$sql = Raid::findOrFail($request['id']);
$sql = $sql->update($request->all());
I have a array in my table , after update value will be like this:
"{\"Plate\":0,\"Cloth\":0,\"Mail\":0,\"Leather\":0}"
but it should be:
{"Plate":"0","Cloth":"0","Mail":"0","Leather":"0"}
so I will get an error
before this , I was updating like this and it was ok:
$sql = Raid::where('id', $request['id'])->update($request->all());
and this is my mode (traders and class_traders is fields that I have problem with):
use SoftDeletes;
use \OwenIt\Auditing\Auditable;
protected $table = 'raid';
protected $dates = ['date_and_time','deleted_at'];
protected $fillable = [
'admin_id', '....
];
protected $casts = [
'bosses' => 'array',
'traders' => 'array',
'class_traders' => 'array',
'boosters' => 'array',
];

Cannot return null for non-nullable field - rebing/graphql

I have a problem with GraphQL(rebing-graphql)/Larvel app. App works fine when I query normal GraphQL query(single not nested), but when I query nested one, I face "debugMessage":"Cannot return null for non-nullable field \"Make Type.name\".
Normal query which works fine:
{model{id,name}}
Nested query that I want to execute:
{model{id,name,make_id{id,name}}
Where am I made mistake?
Thanks in advance.
Make Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\App;
class Make extends Model
{
use HasFactory;
protected $fillable = [
'name',
'logo',
'website',
];
public function models()
{
return $this->hasMany(\App\Models\Model::class);
}
}
Model Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model as MModel;
class Model extends MModel
{
use HasFactory;
protected $fillable = [
'make_id',
'name',
'website',
];
public function make()
{
return $this->belongsTo(Make::class);
}
}
MakeQuery (Graphql part)
<?php
namespace App\GraphQL\Queries;
use App\Models\Make;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;
class MakeQuery extends Query
{
protected $attributes = [
'name' => 'Make Type',
'description' => 'Fetch Make Query'
];
public function args(): array
{
return ["id" => ['type' => Type::int()]];
}
public function type(): type
{
return Type::listOf(GraphQL::type('make'));
}
public function resolve($root, $args)
{
if (isset($args['id'])) {
return Make::where("id",$args['id'])->get();
}
return Make::all();
}
}
MakeType
<?php
namespace App\GraphQL\Types;
use App\Models\Make;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Type as GraphQLType;
class MakeType extends GraphQLType
{
protected $attributes = [
'name' => 'Make Type',
'description' => 'Make API Type',
'model' => Make::class
];
public function fields(): array
{
return [
"id" => [
'type' => Type::nonNull(Type::int()),
'description' => 'Make ID'
],
"name" => [
'type' => Type::nonNull(Type::string()),
'description' => 'Make ID'
],
"logo" => [
'type' => Type::nonNull(Type::string()),
'description' => 'Make ID'
],
"website" => [
'type' => Type::nonNull(Type::string()),
'description' => 'Make ID'
]
];
}
}
ModelQuery
<?php
namespace App\GraphQL\Queries;
use App\Models\Model;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Facades\GraphQL;
use Rebing\GraphQL\Support\Query;
class ModelQuery extends Query
{
protected $attributes = [
'name' => 'Model Type',
'description' => 'Fetch Model Query'
];
public function args(): array
{
return [
"id" => ['type' => Type::int()]
];
}
public function type(): type
{
return Type::listOf(GraphQL::type('model'));
}
public function resolve($root, $args)
{
if (isset($args['id'])) {
return Model::where("id", $args['id'])->get();
}
return Model::all();
}
}
ModelType
<?php
namespace App\GraphQL\Types;
use App\Models\Make;
use App\Models\Model;
use Rebing\GraphQL\Support\Facades\GraphQL;
use GraphQL\Type\Definition\Type;
use Rebing\GraphQL\Support\Type as GraphQLType;
class ModelType extends GraphQLType
{
protected $attributes = [
'name' => 'Model Type',
'description' => 'Model API Type',
'model' => Model::class
];
public function fields(): array
{
return [
"id" => [
'type' => Type::nonNull(Type::int()),
'description' => 'Model ID'
],
"make_id" => [
'type' => GraphQL::type('make'),
'description' => 'Model_ID'
],
"name" => [
'type' => Type::nonNull(Type::string()),
'description' => 'Model Name'
],
"website" => [
'type' => Type::nonNull(Type::string()),
'description' => 'Model website'
]
];
}
}
There are several things that you have to done to get your code works:
First: Be sure that your tables are full and have valid key relations.
Second: In ModelType change make_id to makeId.
Third: Reload composer autoload with composer dump-autoload.
Finally: In your Model Model it's better to define a column like below:
public function makeId()
{
return $this->belongsTo(Make::class, 'make_id', 'id');
}
I hope these steps would help you.

Resources