Laravel model observer method doesn't get fired - laravel

In my Laravel (7.x) I am trying to use Observers for updating device_inventories table if the value of the record if the record of the subscriber_devices table is updated as inactive.
device_inventories | subscriber_devices
----------------------- | --------------------------------
# | title | status | # | device_inventory_id | status
----------------------- | --------------------------------
1 | device-1 | active | 1 | 1 | active
2 | device-2 | active | 2 | 2 | active
For example:
Suppose I update the record id = 2 of subscriber_devices to status = inactive then the value of device_inventories / status should be updated to replacement. Assuming, that the device was damaged and needs to be sent for replacement.
AppServiceProvider.php
use App\SubscriberDevice;
use App\Observers\ObserverChangeDeviceInventoryStatusToReplacement;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
SubscriberDevice::observe(ObserverChangeDeviceInventoryStatusToReplacement::class);
}
}
App\Observers\ObserverChangeDeviceInventoryStatusToReplacement.php
use App\DeviceInventory;
use App\SubscriberDevice;
class ObserverChangeDeviceInventoryStatusToReplacement
{
public function updated(SubscriberDevice $SubscriberDevice)
{
DeviceInventory::where('id', $SubscriberDevice->device_inventory_id)->update([
'status' => 'replacement'
]);
}
}
App\SubscriberDevice.php
class SubscriberDevice extends Model
{
protected $table = 'subscriber_devices';
public function _status($token, $reason)
{
self::where('token', $token)->update([
'reason' => $reason,
'status' => 'inactive',
]);
}
}
The observer method doesn't seems to be firing. What am I doing wrong.?

Thanks to #EmptyBrain.
App\SubscriberDevice.php
class SubscriberDevice extends Model
{
protected $table = 'subscriber_devices';
public function _status($token, $reason)
{
$self = self::where('token', $token)->first();
$self->update([
'reason' => $reason,
'status' => 'inactive',
]);
}
}
Reference laravel Eloquent model update event is not fired

Related

Laravel eloquent get count on belongsTo id

I have 2 tables subscribers & subscriber_packages. I need to fetch the count of currently active packages for the selected subscriber via given subscriber_package / id.
Tables:
# | subscriber_id | package_id | active
---------------------------------------
1 | 1 | 1 | true
2 | 1 | 2 | true
3 | 1 | 3 | true
4 | 1 | 4 | false
---------------------------------------
App\SubscriberPackage.php
class SubscriberPackage extends Model
{
public function subscriber()
{
return $this->belongsTo(Subscriber::class);
}
public function package()
{
return $this->belongsTo(Package::class);
}
public function _countActivePackages($subscriberPackage)
{
return self::where([
'id' => $subscriberPackage,
'status' => \Common::STATUS_ACTIVE,
])->whereHas('subscriber')->count();
}
}
At your subscriber you need to define correctly a HasManyThrough relationship and just add properly a where cause to check the status even in separate method
public function packages()
{
return $this->hasManyThrough(Package::class, SubscriberPackage::class);
}
public function activePackages()
{
return $this->packages()
->where("subscriber_package.status", "=", "active");
}
After that you can always make a count with it
$subscriber->activePackages()->count();

Asking an advise about Pivot Table

I have 3 tables like this
Student:
id | name
class Student extends Model
{
protected $table = 'students';
public function marks () {
return $this->belongsToMany( 'App\Models\Subject', 'marks' )->using( 'App\Models\Mark');
}
}
Subject:
id | name
class Subject extends Model
{
protected $table = 'subjects';
public function marks () {
return $this->belongsToMany( 'App\Models\Student', 'marks' )->using( 'App\Models\Mark');
}
}
Mark:
subject_id | student_id | type_of_exam | times_taking_exam | mark
class Mark extends Pivot
{
protected $table = 'marks';
public function student() {
return $this->belongsTo('App\Models\Student','student_id');
}
public function subject() {
return $this->belongsTo('App\Models\Student','subject_id');
}
public $timestamps = false;
}
The table Mark which has 4 column primary key (id_subject, id_student, type_of_exam, times_taking_exam)
I don't know my design database is correct or not, but if it did, how can I use sync to update or insert mark for my student.
Because I understand that the method sync is like this:
$student->marks()->sync([
$subject_id => [
'type_of_exam' => 1,
'times_taking_exam' => 1,
'mark' => 10,
]
]);
But it is not correct for this situation cause it will run this code like this:
Mark::updateOrCreate( [
'student_id' => $student_id,
'subject_id' => $subject_id,
], [
'type_of_exam' => 1,
'times_taking_exam' => 1,
'mark' => 10
] );
Instead, I want this:
Mark::updateOrCreate( [
'student_id' => $student_id,
'subject_id' => $subject_id,
'type_of_exam' => 1,
'times_taking_exam' => 1,
], [
'mark' => 10
] );
I have tried use Mark as Model but I think it's not the best way.

How use conditional relationship in eloquent laravel

I have a 'conversation_message' table and separate sender role by column 'sender_type' (admin/user). Both of admin & user in a different table. But when I call the model, that showed error Call to a member function addEagerConstraints() on null
Table column and data
| id | id_group | id_reply | id_sender | sender_type | message
| 1 | 1 | null | 3 | admin | Hi, I'm admin
| 2 | 1 | 1 | 3 | admin | I wanna give u promo
| 3 | 1 | 2 | 18 | user | What's promo ?
I've tried if conditional with column value, but it doesn't work.
Conversation_message.php
public function sender(){
switch($this->sender_type){
case "user":
return $this->belongsTo(User::class, 'id_sender', 'id');
case "admin":
return $this->belongsTo(Admin::class, 'id_sender', 'id');
default:
return;
}
}
InboxController.php
public function message_detail_chat($groupId){
$data = [];
$data['messages'] = Conversation_message::with('replies')
->with('sender')
->with('recipients')
->where(['id_group' => $groupId])->get();
}
I expect to use conditional model by column value 'sender_type' but the actual output is wrong.
laravel provide query scope your can read it from here https://laravel.com/docs/5.8/eloquent#local-scopes
function userSender(){
$this->belongsTo(User::class, 'id_sender', 'id');
}
function adminSender(){
return $this->belongsTo(Admin::class, 'id_sender', 'id');
}
public function scopeSender($query)
{
return $query
->when($this->sender_type === 'user',function($q){
return $q->with('userSender');
})
->when($this->sender_type === 'admin',function($q){
return $q->with('adminSender');
});
}
Now you can access your Sender like this
Conversation_message::Sender()->first();
This should give you the right Sender. Hope it helps.

Updating a Whole Table (Laravel and Vuejs)

I have a table on admin panel which contains 60 rows and 10 columns. Those columns belongs 2 different tables in database. Simplified version is like so:
| Asset | Trade | Status | Code | Link |
Code and Link columns contain empty text inputs. When I enter anything, their values go to Vue.js object and I save them in the Posts table which works perfectly fine.
Trade and Status columns have radio buttons and they belong to Trades table. When I check the radio buttons, their values go to Vue.js object. When I try to update the Trades table it returns 405 error.
The button calls that function:
publish() {
this.form.post("/api/posts")
.then(() => {
this.form
.put("/api/trades")
})
.catch(() => {
//error codes...
});
}
I don't know if POST and PUT requests can be used together, or if this is the correct syntax, but I like to make both action with one click.
My update function is down below. As you can see I'm trying to use "asset_id" which is the foreign key in the Trades table, instead of "id" which is primary key of Trades table. I think this causes the problem, but I'm not sure. I also add that line of code to Post modal file, but it didn't help:
protected $primaryKey = 'asset_id';
My update function:
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $asset_id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $asset_id)
{
$input = $request->all();
if (count($input['asset_id']) > 0) {
foreach ($input['asset_id'] as $asset_id) {
if (array_key_exists($asset_id, $input['status'])) {
$updates = [
'trade' => $input['trade'][$asset_id],
'status' => $input['status'][$asset_id],
'private' => (array_key_exists($asset_id, $input['public_code']) ? 0 : 1)
];
Post::where('asset_id', $asset_id)->find($asset_id)->update($updates);
}
}
}
}
When I check the radio buttons and fill the inputs Vue.js object becomes something like this:
form: new Form([
asset_id: [2, 5, 16, 52],
trade: {
2: "Long",
5: "None",
16: "Long",
52: "Short"
},
status: {
2: "Active",
5: "Canceled",
16: "Pending"
52: "Active"
},
public_code: {
2: "VBFABVR",
16: "hmbtr46"
},
link: {
2: "http://...",
16: : "http://..."
}
])
I'm not sure if I should provide more details. If it's so, please let me know. I hope someone tells me what is wrong here.
EDIT:
Route codes:
Route::apiResources([
'users' => 'API\UsersController',
'roles' => 'API\RolesController',
'plans' => 'API\PlansController',
'categories' => 'API\CategoriesController',
'assets' => 'API\AssetsController',
'posts' => 'API\PostsController',
'trades' => 'API\TradesController',
]);
Route List:
| POST | api/trades | trades.store | App\Http\Controllers\API\TradesController#store | api
| GET|HEAD | api/trades | trades.index | App\Http\Controllers\API\TradesController#index | api
| PUT|PATCH | api/trades/{trade} | trades.update | App\Http\Controllers\API\TradesController#update | api
| GET|HEAD | api/trades/{trade} | trades.show | App\Http\Controllers\API\TradesController#show | api
| DELETE | api/trades/{trade} | trades.destroy | App\Http\Controllers\API\TradesController#destroy | api
It was a rookie mistake. I was trying to pass a collection of the trades to database, but put request requires /api/trades/$id. That's why I've created a for loop in front-end, and in back-end, all I need was a simple update function.
This is the function that is called when I click the submit button:
publish() {
var trades = this.form.asset_id.length;
this.form.post("/api/posts")
for (var i = 0; i < trades; i++) {
if (this.form.asset_id[i] in this.form.status) {
this.form.put("/api/trades/" + this.form.asset_id[i])
}
}
}
And this is the upload function:
public function update(Request $request, $id)
{
$item = Trade::findOrFail($id);
$input = $request->all();
$updates = [
'trade' => $input['trade'][$id],
'status' => $input['status'][$id],
'private' => (array_key_exists($id, $input['public_code']) ? 0 : 1)
];
$item->update($updates);
}

Share value from database in all view in laravel 5

I'm new to laravel. I'm trying to create a simple app with some variable that shouold be in every view. So I create a setting model, with many fields and a unique slug field. Now to share this variable from database I've created a middleware:
public function handle($request, Closure $next)
{
$site_settings = Cache::remember('settings', 60, function() {
return Setting::all();
});
view()->share('site_settings', $site_settings);
return $next($request);
}
Now to show this variable in view I have:
{{{ $site_settings->get(0)->value }}}
This works great, but I'd like to have a more intuitive code in my view, accessing the setting by slug. Something like:
{{{ $site_settings->findBySlug("MYVARIABLE")->value }}}
So it's possible to filter collection by an unique slug?
Info:
This assumes your settings table structure is as following:
----------------------
| id | key | value |
----------------------
| 1 | titleĀ | foo |
| 2 | asd | bar |
| 3 | qqq | zzz |
----------------------
Steps
Step 1: Put the following newCollection method into App\Setting model:
class Settings extends ... {
public function newCollection(array $models = array())
{
return new \App\Collections\SettingsCollection($models);
}
}
Step 2: Put the following lines into App\Collections\SettingsCollection class:
<?php namespace App\Collections;
use Illuminate\Database\Eloquent\Collection;
class SettingsCollection extends Collection {
public function get($name, $default = null)
{
return array_first($this->items, function($itemKey, $model) use ($name)
{
return $model->key == $name;
}, $default)->value;
}
}
Step 3: Enjoy! This is the collection I'm currently using. Instead of this:
$settings = Setting::all();
$settings->where('key', 'key_name')->first()->value;
You can simply just do this:
$settings = Setting::all();
echo $settings->get('key_name'); // returns value of the setting with the key 'title'
Maybe a query scope would fit.
In your model:
public function scopeFindBySlug($slug){
return $query->where('slug','=', $slug);
}
So you will have: $site_settings->findBySlug('your_slug')->get();

Resources