Model observer is not firing while updating - php

I have an issue while trying to use an observer that it doesn't fire the observer only in the updated case. it works fine in other cases !.
I had some research about it and I found some solutions, such as calling the update method directly from the model, not in a repository, but unfortunately, it didn't work for me.
the solution I found: https://github.com/laravel/framework/issues/11777#issuecomment-170388067
UserObserver
class UserObserver
{
/**
* Handle the user "created" event.
*
* #param \App\Models\User $user
* #return void
*/
public function created(User $user)
{
dd($user);
}
/**
* Handle the user "updated" event.
*
* #param \App\Models\User $user
* #return void
*/
public function updated(User $user)
{
dd($user);
}
/**
* Handle the user "deleted" event.
*
* #param \App\Models\User $user
* #return void
*/
public function deleted(User $user)
{
dd($user);
}
}
and here's my AppServiceProvider:
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
User::observe(UserObserver::class);
}
}
I called the update method in my controller like this:
// First try
$user = User::findOrFail($id);
$user->update($data);
// Second try
User::findOrFail($id)->update($data);
this an example of my $data array:
array [
"nationality_id" => "1"
"birth_date" => "2013-05-26"
"gender" => "1"
"job_title" => "Pariatur Sit provideeee"
"salary" => "5100"
]

Laravel checks if the model has actually been changed before firing updates, so make sure you change the model before saving or updating it.
Additionally, updating events are only fired when you update your model directly.
That means this will fire an event:
$user = User::findOrFail($id);
$user->update($data);
While something like this will not
User::where('id', $id)->update($data);
This is done by design, since using an update query could update millions of rows at once, making it unrealistic to fire events for all of them.

Related

Laravel observer property not being called on update

I am looking into observers and I found out that instead of calling an observer during the boot() method of the App\Providers\EventServiceProvider laravel can call it using the property protected $observers.
It works perfectly fine when calling it in the boot method, but it does not work when I put it in the property.
What am I doing wrong? I could not find any other explanation in the documentation.
My eventServiceProvider:
class EventServiceProvider extends ServiceProvider
{
/**
* Summary of Observers
* #var mixed
*/
protected $observers = [
User::class => [UserObserver::class],
];
/**
* Register any events for your application.
*
* #return void
*/
public function boot()
{
//User::observe(UserObserver::class);
//Above line is commented because property $observers is used, but uncommented when property observers is commented.
}
My observer:
class UserObserver
{
/**
* Handle the User "updated" event.
*
* #param \App\Models\User $user
* #return void
*/
public function updated(User $user)
{
$dirty = $user->getChanges();
dump($user);
dd($dirty);
}
}

How would one construct a complex/composite controller for multiple controllers

I am new to OOP/MVC and I do have a basic understanding of a controller which interacts with an underlying model. Basically, a controller acts as a "CRUD gateway" to a model. However, consider an e-commerce marketplace object: Order.
An e-commerce order in a marketplace would interact with multiple tables and hence an order can be thought of as a join of multiple tables: orders, order_items, order_sellers, order_buyer (and more perhaps).
If I understand it correctly, each one of these tables would have a controller allowing CRUD operations (OrderInfoController, OrderItemController,OrderSellerController,OrderBuyerController etc.).
However, could I also create a controller for 'Orders' which then instantiates the Controller Object for each of the tables involved in an Order?
OrderController {
$this->orderInfo = OrderInfo Object;
$this->orderItems = array of Order Item Objects;
$this->orderSellers = array of Order Seller Objects;
$this->orderBuyer = OrderBuyer Object;
function create($arr_order)
//create the order object by calling each of the member controllers.
function get($orderId)
//get the complete order by order Id....
function update($orderId)
function delete($orderId)
}
I have gone through a few MVC docs but I have not come across a solution to this problem. My question is then: Is this the correct approach to write a controller which interacts with multiple tables?
To
If I understand it correctly, each one of these tables would have a controller allowing CRUD operations [...].
In a web MVC-based application, each request is, indeed, served by a controller (the "C" in "MVC").
Though, the controller delegates the whole processing of the request to one or more application services (e.g. use cases, e.g actions - see resources list below), as part of the service layer. These services interact with the model (the "M" in "MVC"), e.g. domain model, e.g. model layer, which, in turn, interact with the database.
The final result of the processing of the request data, e.g. the response object, is either returned to the controller, in order to be passed and printed on screen by the view (the "V" in "MVC"), or directly to the view, for the same reason.
After watching both videos in the resources list below, you will understand, that the model doesn't need to know anything about the database. So, the components of the model layer (mostly interfaces) should not know where and how the data passed to them by the services is saved. Therefore, the services and the controllers should also know nothing about the database.
All informations regarding the database should be located in data mappers only - as part of the infrastructure layer. These objects should be the only ones understanding the database API. Therefore, the only ones containing and beeing able to execute SQL statements.
To
Is this the correct approach to write a controller which interacts with multiple tables?
No. But it's not a problem. Just keep learning about MVC.
Resources:
Keynote: Architecture the Lost Years by Robert Martin.
Sandro Mancuso : Crafted Design
Here is some code of mine. At first sight, it's maybe a lot of it, but I'm confident, that it will help you to better understand.
For simplicity, follow the definition of the method getAllUsers in the view class SampleMvc\App\View\Template\Users\Users.
First of all, here is a not so important note (yet): In my code, the controller only updates the model layer, and the view only fetches data from the model layer. Only the response returned by the view is, therefore, printed. The controller and the view are called by a class RouteInvoker, like this:
<?php
namespace MyPackages\Framework\Routing;
//...
class RouteInvoker implements RouteInvokerInterface {
//...
public function invoke(RouteInterface $route): ResponseInterface {
$controller = $this->resolveController($route);
$view = $this->resolveView($route);
$parameters = $route->getParameters();
$this->callableInvoker->call($controller, $parameters);
return $this->callableInvoker->call($view, $parameters);
}
//...
}
The result ($response) of RouteInvoker:invoke is printed like this:
$responseEmitter->emit($response);
And from here follows an example of a code invoked by RouteInvoker:invoke:
A controller to handle the users:
<?php
namespace SampleMvc\App\Controller\Users;
use function sprintf;
use SampleMvc\App\Service\Users\{
Users as UserService,
Exception\UserExists,
};
use Psr\Http\Message\ServerRequestInterface;
/**
* A controller to handle the users.
*/
class Users {
/**
*
* #param UserService $userService A service to handle the users.
*/
public function __construct(
private UserService $userService
) {
}
/**
* Add a user.
*
* #param ServerRequestInterface $request A server request.
* #return void
*/
public function addUser(ServerRequestInterface $request): void {
$username = $request->getParsedBody()['username'];
try {
$this->userService->addUser($username);
} catch (UserExists $exception) {
//...
}
}
/**
* Remove all users.
*
* #return void
*/
public function removeAllUsers(): void {
$this->userService->removeAllUsers();
}
}
A view to handle the users:
Notice, that controller and view share the same UserService instance.
<?php
namespace SampleMvc\App\View\Template\Users;
use SampleMvc\App\{
View\Layout\Primary,
Service\Users\Users as UserService,
Components\Service\MainNavigation,
};
use Psr\Http\Message\{
ResponseInterface,
ResponseFactoryInterface,
};
use AlePackages\Template\Renderer\TemplateRendererInterface;
/**
* A view to handle the users.
*/
class Users extends Primary {
/**
*
* #param UserService $userService A service to handle the users.
*/
public function __construct(
ResponseFactoryInterface $responseFactory,
TemplateRendererInterface $templateRenderer,
MainNavigation $mainNavigationService,
private UserService $userService
) {
parent::__construct($responseFactory, $templateRenderer, $mainNavigationService);
}
/**
* Display the list of users.
*
* #return ResponseInterface The response to the current request.
*/
public function default(): ResponseInterface {
$bodyContent = $this->templateRenderer->render('#Templates/Users/Users.html.twig', [
'activeNavItem' => 'Users',
'users' => $this->getAllUsers(),
]);
$response = $this->responseFactory->createResponse();
$response->getBody()->write($bodyContent);
return $response;
}
/**
* Add a user.
*
* #return ResponseInterface The response to the current request.
*/
public function addUser(): ResponseInterface {
$bodyContent = $this->templateRenderer->render('#Templates/Users/Users.html.twig', [
'activeNavItem' => 'Users',
'message' => 'User successfully added',
'users' => $this->getAllUsers(),
]);
$response = $this->responseFactory->createResponse();
$response->getBody()->write($bodyContent);
return $response;
}
/**
* Remove all users.
*
* #return ResponseInterface The response to the current request.
*/
public function removeAllUsers(): ResponseInterface {
$bodyContent = $this->templateRenderer->render('#Templates/Users/Users.html.twig', [
'activeNavItem' => 'Users',
'message' => 'All users successfully removed',
'users' => $this->getAllUsers(),
]);
$response = $this->responseFactory->createResponse();
$response->getBody()->write($bodyContent);
return $response;
}
/**
* Get a list of users.
*
* #return (string|int)[][] The list of users.
*/
private function getAllUsers(): array {
$users = $this->userService->findAllUsers();
$usersFormatted = [];
foreach ($users as $user) {
$usersFormatted[] = [
'id' => $user->getId(),
'username' => $user->getUsername(),
];
}
return $usersFormatted;
}
}
A service to handle the users:
<?php
namespace SampleMvc\App\Service\Users;
use SampleMvc\Domain\Model\User\{
User,
UserCollection,
};
use SampleMvc\App\Service\Users\Exception\UserExists;
/**
* A service to handle the users.
*/
class Users {
/**
*
* #param UserCollection $userCollection A collection of users.
*/
public function __construct(
private UserCollection $userCollection
) {
}
/**
* Find a user by id.
*
* #param int $id An id.
* #return User|null The found user or null.
*/
public function findUserById(int $id): ?User {
return $this->userCollection->findById($id);
}
/**
* Find all users.
*
* #return User[] The list of users.
*/
public function findAllUsers(): array {
return $this->userCollection->all();
}
/**
* Add a user.
*
* #param string|null $username A username.
* #return User The added user.
*/
public function addUser(?string $username): User {
$user = $this->createUser($username);
return $this->storeUser($user);
}
/**
* Remove all users.
*
* #return void
*/
public function removeAllUsers(): void {
$this->userCollection->clear();
}
/**
* Create a user.
*
* #param string|null $username A username.
* #return User The user.
*/
private function createUser(?string $username): User {
$user = new User();
$user->setUsername($username);
return $user;
}
/**
* Store a user.
*
* #param User $user A user.
* #return User The stored user.
* #throws UserExists A user already exists.
*/
private function storeUser(User $user): User {
if ($this->userCollection->exists($user)) {
throw new UserExists('Username "' . $user->getUsername() . '" already used');
}
return $this->userCollection->store($user);
}
}
An exception indicating that a user already exists:
<?php
namespace SampleMvc\App\Service\Users\Exception;
/**
* An exception indicating that a user already exists.
*/
class UserExists extends \OverflowException {
}
An interface to a collection of users:
Notice, that this is an interface.
Notice, that this interface is a component of the domain model!
Notice, that its implementation (e.g. SampleMvc\Domain\Infrastructure\Repository\User\UserCollection further down below) is not part of the domain model, but of the infrastructure layer!
<?php
namespace SampleMvc\Domain\Model\User;
use SampleMvc\Domain\Model\User\User;
/**
* An interface to a collection of users.
*/
interface UserCollection {
/**
* Find a user by id.
*
* #param int $id An id.
* #return User|null The found user or null.
*/
public function findById(int $id): ?User;
/**
* Get all users from the collection.
*
* #return User[] All users in the collection.
*/
public function all(): array;
/**
* Store a user.
*
* #param User $user A user.
* #return User The stored user.
*/
public function store(User $user): User;
/**
* Check if a user exists in the collection.
*
* #param User $user A user.
* #return bool True if the user exists, or false otherwise.
*/
public function exists(User $user): bool;
/**
* Remove all users from the collection.
*
* #return static
*/
public function clear(): static;
}
A collection of users:
<?php
namespace SampleMvc\Domain\Infrastructure\Repository\User;
use SampleMvc\Domain\Model\User\{
User,
UserCollection as UserCollectionInterface,
};
use SampleMvc\Domain\Infrastructure\Mapper\User\UserMapper;
/**
* A collection of users.
*/
class UserCollection implements UserCollectionInterface {
/**
*
* #param UserMapper $userMapper A user mapper.
*/
public function __construct(
private UserMapper $userMapper
) {
}
/**
* #inheritDoc
*/
public function findById(int $id): ?User {
return $this->userMapper->fetchUserById($id);
}
/**
* #inheritDoc
*/
public function all(): array {
return $this->userMapper->fetchAllUsers();
}
/**
* #inheritDoc
*/
public function store(User $user): User {
return $this->userMapper->saveUser($user);
}
/**
* #inheritDoc
*/
public function exists(User $user): bool {
return $this->userMapper->userExists($user);
}
/**
* #inheritDoc
*/
public function clear(): static {
$this->userMapper->deleteAllUsers();
return $this;
}
}
An interface to a user mapper:
Notice that this is the interface of a data mapper.
<?php
namespace SampleMvc\Domain\Infrastructure\Mapper\User;
use SampleMvc\Domain\Model\User\User;
/**
* An interface to a user mapper.
*/
interface UserMapper {
/**
* Fetch a user by id.
*
* Note: PDOStatement::fetch returns FALSE if no record is found.
*
* #param int $id A user id.
* #return User|null The user or null.
*/
public function fetchUserById(int $id): ?User;
/**
* Fetch all users.
*
* #return User[] The list of users.
*/
public function fetchAllUsers(): array;
/**
* Save a user.
*
* #param User $user A user.
* #return User The saved user.
*/
public function saveUser(User $user): User;
/**
* Check if a user exists.
*
* Note: PDOStatement::fetch returns FALSE if no record is found.
*
* #param User $user A user.
* #return bool True if the user exists, or false otherwise.
*/
public function userExists(User $user): bool;
/**
* Delete all users.
*
* #return static
*/
public function deleteAllUsers(): static;
}
A PDO user mapper:
Notice, that this component is the implementation of a data mapper.
Notice, that this component is the only one understanding the database API. Therefore, the only one containing and beeing able to execute SQL statements.
Notice, that this component is not part of the domain model, but of the infrastructure layer!
(1) Notice, that you can write any SQL statements that you want, including JOIN statements. So, the fetched data can come from multiple tables as well.
(2) Notice also, that the result of a method of this class could be a list of objects of a type defined by you (!), independent of the underlying table(s) data..
The conclusion from (1) and (2) above: The database structure does NOT affect in any way the way in which your application is structured.
<?php
namespace SampleMvc\Domain\Infrastructure\Mapper\User;
use SampleMvc\Domain\{
Model\User\User,
Infrastructure\Mapper\User\UserMapper,
};
use PDO;
/**
* A PDO user mapper.
*/
class PdoUserMapper implements UserMapper {
/**
*
* #param PDO $connection A database connection.
*/
public function __construct(
private PDO $connection
) {
}
/**
* #inheritDoc
*/
public function fetchUserById(int $id): ?User {
$sql = 'SELECT * FROM users WHERE id = :id LIMIT 1';
$statement = $this->connection->prepare($sql);
$statement->execute([
'id' => $id,
]);
$dataArray = $statement->fetch(PDO::FETCH_ASSOC);
return ($dataArray === false) ? null : $this->convertDataArrayToUser($dataArray);
}
/**
* #inheritDoc
*/
public function fetchAllUsers(): array {
$sql = 'SELECT * FROM users';
$statement = $this->connection->prepare($sql);
$statement->execute();
$listOfDataArrays = $statement->fetchAll(PDO::FETCH_ASSOC);
return $this->convertListOfDataArraysToListOfUsers($listOfDataArrays);
}
/**
* #inheritDoc
*/
public function saveUser(User $user): User {
return $this->insertUser($user);
}
/**
* #inheritDoc
*/
public function userExists(User $user): bool {
$sql = 'SELECT COUNT(*) as cnt FROM users WHERE username = :username';
$statement = $this->connection->prepare($sql);
$statement->execute([
':username' => $user->getUsername(),
]);
$data = $statement->fetch(PDO::FETCH_ASSOC);
return ($data['cnt'] > 0) ? true : false;
}
/**
* #inheritDoc
*/
public function deleteAllUsers(): static {
$sql = 'DELETE FROM users';
$statement = $this->connection->prepare($sql);
$statement->execute();
return $this;
}
/**
* Insert a user.
*
* #param User $user A user.
* #return User The user, with updated id.
*/
private function insertUser(User $user): User {
$sql = 'INSERT INTO users (username) VALUES (:username)';
$statement = $this->connection->prepare($sql);
$statement->execute([
':username' => $user->getUsername(),
]);
$user->setId($this->connection->lastInsertId());
return $user;
}
/**
* Update a user.
*
* #param User $user A user.
* #return User The user.
*/
private function updateUser(User $user): User {
$sql = 'UPDATE users SET username = :username WHERE id = :id';
$statement = $this->connection->prepare($sql);
$statement->execute([
':username' => $user->getUsername(),
':id' => $user->getId(),
]);
return $user;
}
/**
* Convert the given data array to a user.
*
* #param array $dataArray A data array.
* #return User The user.
*/
private function convertDataArrayToUser(array $dataArray): User {
$user = new User();
$user
->setId($dataArray['id'])
->setUsername($dataArray['username'])
;
return $user;
}
/**
* Convert the given list of data arrays to a list of users.
*
* #param array[] $listOfDataArrays A list of data arrays.
* #return User[] The list of users.
*/
private function convertListOfDataArraysToListOfUsers(array $listOfDataArrays): array {
$listOfUsers = [];
foreach ($listOfDataArrays as $dataArray) {
$listOfUsers[] = $this->convertDataArrayToUser($dataArray);
}
return $listOfUsers;
}
}

How can I fix the `Event Listener` problem when I update the shop table in laravel?

Now I need to send message to User whose shop is activated. And also give them a dashboard to add new products.
Goal: When in shops table the value of column is_active is made to active from inactive then I need to fire the email to user.
Shop.php
public function seller() //user --> seller
{
return $this->belongsTo(User::class, 'user_id');
}
I need observer class so I can find all these information on documentation in event section of Eloquent.
Step 1
Run below command to Create observer class for Shop Model
~$ php artisan make:observer ShopObserver --model=Shop
Now we have DealOcean\app\Observers\ShopObserver.php
ShopObserver.php
<?php
namespace App\Observers;
use App\Shop;
class ShopObserver
{
/**
* Handle the shop "created" event.
*
* #param \App\Shop $shop
* #return void
*/
public function created(Shop $shop)
{
//
}
/**
* Handle the shop "updated" event.
*
* #param \App\Shop $shop
* #return void
*/
public function updated(Shop $shop)
{
//
}
/**
* Handle the shop "deleted" event.
*
* #param \App\Shop $shop
* #return void
*/
public function deleted(Shop $shop)
{
//
}
/**
* Handle the shop "restored" event.
*
* #param \App\Shop $shop
* #return void
*/
public function restored(Shop $shop)
{
//
}
/**
* Handle the shop "force deleted" event.
*
* #param \App\Shop $shop
* #return void
*/
public function forceDeleted(Shop $shop)
{
//
}
}
Step 2
Before doing that, we need to tell Laravel that you need to use this class whenever model events related to Shop is updated. To register an observer, use the observe method on the model you wish to observe. You may register observers in the boot method of one of your service providers. In this example, we'll register the observer in the AppServiceProvider:
Go to AppServiceProvider
AppServiceProvider.php
use App\Shop;
use App\Observers\ShopObserver;
public function boot()
{
Shop::observe(ShopObserver::class);
}
Step 3
ShopObserver.php
public function updated(Shop $shop)
{
dd($shop);
}
SO when I update the is_active column to active from inactive then this doesn't work.
I mean the it should call the update function. But this function is not called.
I can't figure out the problem.
ShopController.php
public function update(Request $request, Shop $shop)
{
Shop::where('id', $request->shop_id)
->update([
'is_active' => $request->is_active,
'description' => $request->description,
'location_id' => $request->location
]);
return Redirect::route('dashboard.shops');
}
If you take a look at the docs, this is called mass update because the models are not retrieved so events are not fired. You should find the shop first then update its details like this.
$updatingShop = Shop::where('id', $request->shop_id)->first();
if($updatingShop) {
$updatingShop->update([
'is_active' => $request->is_active,
'description' => $request->description,
'location_id' => $request->location
]);
}
In updated method of observer you can compare old value with new value.
Also, my suggestion would be to use event/listener methodology there so you can have something similar to this:
/**
* Handle the User "updated" event.
*
* #param \App\Shop $shop
* #return void
*/
public function updated(Shop $shop)
{
if ($shop->is_active === 'active'
&& $shop->is_active !== $shop->getOriginal('is_active')) {
// you can make quick and dirty way here
// but correct and well done job would be if you set event/listener
event(new ShopUpdatedToActive());
}
}
You can always check old values with some of methods from this trait (isDirty, isClean, wasChanged, hasChanges, getOriginal etc.)
Do you update the Shop model or the User model? Pay attention, you subscribed to the Shop model.
Advice:
Try to avoid Observers, as they introduce some magic to the requests flow.
I prefer to use Events and Event Listeners to do such a job.
https://laravel.com/docs/7.x/events
just use the $shop in your controller update method to update
public function update(Request $request, Shop $shop)
{
$shop
->update([
'is_active' => $request->is_active,
'description' => $request->description,
'location_id' => $request->location
]);
return Redirect::route('dashboard.shops');
}
this will fire the observer class
the problem that you are using where on the model it self and that is a mass update, that will not fire the observer
#edit
if you want the observe to work only on the is_active field
you should edit
in your observe class add that:
public function updated(Shop $shop)
{
if($shop->isDirty('is_active')){
// add your code
}
}

No query results for model [App\Models\Match]

I'm building an API with Laravel and want to send push notification using the Laravel Notifications system. I've a model for matches (which is basically a post), another user can like this match. When the match is liked, the creator of the post will get a push notification. It's just like Instagram, Facebook, etc.
Often the push notification wasn't send to the user. I installed Laravel Horizon to see if there where errors. Sometimes the notification was send and sometimes it wasn't. With the exact same data:
The notification fails sometimes with the exact same data (same user, same match).
The error is as followed:
Illuminate\Database\Eloquent\ModelNotFoundException: No query results
for model [App\Models\Match] 118 in
/home/forge/owowgolf.com/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:312
I'm sure the match and the user exists in the database, I've verified that before sending the notification. Does anybody know what's going wrong? Everything I could find online is that people didn't save their model before sending the notification into the queue. But the line where the code send's the notification into the queue wouldn't even be reached if the model didn't exists. Because of Implicit Binding in the route/controller.
Controller method:
/**
* Like a match.
*
* #param \App\Models\Match $match
* #return \Illuminate\Http\JsonResponse
*/
public function show(Match $match)
{
$match->like();
$players = $match->players()->where('user_id', '!=', currentUser()->id)->get();
foreach ($players as $user) {
$user->notify(new NewLikeOnPost($match, currentUser()));
}
return ok();
}
Notification:
<?php
namespace App\Notifications;
use App\Models\Match;
use App\Models\User;
use Illuminate\Bus\Queueable;
use NotificationChannels\Apn\ApnChannel;
use NotificationChannels\Apn\ApnMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
class NewLikeOnPost extends Notification implements ShouldQueue
{
use Queueable;
/**
* The match instance.
*
* #var \App\Models\Match
*/
private $match;
/**
* The user instance.
*
* #var \App\Models\User
*/
private $user;
/**
* Create a new notification instance.
*
* #param \App\Models\Match $match
* #param \App\Models\User $user
*/
public function __construct(Match $match, User $user)
{
$this->user = $user;
$this->match = $match;
$this->onQueue('high');
}
/**
* Get the notification's delivery channels.
*
* #param \App\Models\User $notifiable
* #return array
*/
public function via($notifiable)
{
if ($notifiable->wantsPushNotification($this)) {
return ['database', ApnChannel::class];
}
return ['database'];
}
/**
* Get the mail representation of the notification.
*
* #param \App\Models\User $notifiable
* #return \NotificationChannels\Apn\ApnMessage
*/
public function toApn($notifiable)
{
return ApnMessage::create()
->badge($notifiable->unreadNotifications()->count())
->sound('success')
->body($this->user->username . ' flagged your match.');
}
/**
* Get the array representation of the notification.
*
* #param mixed $notifiable
* #return array
*/
public function toArray($notifiable)
{
return [
'user_id' => $this->user->id,
'body' => "<flag>Flagged</flag> your match.",
'link' => route('matches.show', $this->match),
'match_id' => $this->match->id,
];
}
/**
* Get the match attribute.
*
* #return \App\Models\Match
*/
public function getMatch()
{
return $this->match;
}
}
This is not a complete solution, but it will lower your chances of running into this error in the future.
Instead of passing in the whole Match model into the job, only pass the id of the model. You can then fetch that model in the constructor.
/**
* Like a match.
*
* #param \App\Models\Match $match
* #return \Illuminate\Http\JsonResponse
*/
public function show(Match $match)
{
$match->like();
$players = $match->players()->where('user_id', '!=', currentUser()->id)->get();
foreach ($players as $user) {
$user->notify(new NewLikeOnPost($match->id, currentUser()->id));
}
return ok();
}
Notification:
class NewLikeOnPost extends Notification implements ShouldQueue
{
use Queueable;
private const QUEUE_NAME = 'high';
/**
* The match instance.
*
* #var \App\Models\Match
*/
private $match;
/**
* The user instance.
*
* #var \App\Models\User
*/
private $user;
/**
* Create a new notification instance.
*
* #param int $match
* #param int $user
*/
public function __construct(int $matchId, int $userId)
{
$this->user = User::query()->where('id', $userId)->firstOrFail();
$this->match = Match::query()->where('id', $matchId)->firstOrFail();
$this->onQueue(self::QUEUE_NAME);
}
// Rest of the class is still the same...
}
You can use the SerializesModels trait, but it doesn't work well when you add a delay to a queued job. This is because it will try to reload the model on __wakeup() and sometimes it cannot find the class.
Hopefully this helps :)
Its probably because $user is not an object of User model, its an object of Match model. You need to do a User::findorfail or User::firstOrFail then notify the user.
public function show(Match $match)
{
$match->like();
$players = $match->players()->where('user_id', '!=', currentUser()->id)->get();
foreach ($players as $user) {
$someUser = User::findOrFail($user->user_id);
$someUser->notify(new NewLikeOnPost($match, currentUser()));
}
return ok();
}
Unless the notify trait is used in Match model. Or you could use eager loading which will cost way less queries!
Check your .env to be sure that u really use REDIS
BROADCAST_DRIVER=redis
CACHE_DRIVER=redis
SESSION_DRIVER=redis
SESSION_LIFETIME=120
QUEUE_DRIVER=redis
then clear cache ( php artisan cache:clear , php artisan view:clear ), that should clear the issue
EDIT
I had similar problems but now I use Docker only and before I had to check for cached configfiles, wrong file/folderpermissions and so on (REDIS for broadcast only, others were standard). I started using redis only - that`s a lot easier, faster and more debugfriendly for me ! And together with Docker really helpful to not use messed up nginx/apache/php/redis/ ...

laravel how to handle profile pages?

I am playing around with Laravel 5. I am trying to build a site where a user can add some information about himself and it shows up in the frontend.
I am struggling to understand how to save the profile information only once.
Everytime the user call /profile/create a new DB entry is created. But I only need one profile entry per user!
If I don't provide a /profile/create route how can a user save his profile info to the DB? As the user can't call profile/edit because no entry exists.
This is my Controller:
namespace App\Http\Controllers;
use App\User;
use App\Profile;
use App\Http\Requests\ProfileRequest;
use App\Http\Requests;
use Illuminate\Http\Request;
use Auth;
class ProfilesController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
return view('backend.profile.index');
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
return view('backend.profile.create');
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(ProfileRequest $request)
{
$profile = new Profile($request->all());
Auth::user()->profiles()->save($profile);
return 'saved';
}
/**
* Display the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
//
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param int $id
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
//
}
/**
* Remove the specified resource from storage.
*
* #param int $id
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
//
}
}
My Profile Model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Profile extends Model
{
protected $fillable = [
'name',
];
public function user() {
$this->belongsTo('App\User');
}
}
My User Model:
/**
* A User can have one Preference
* #return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function profiles() {
return $this->hasOne('App\Profile');
}
There are numerous ways, one is simply throw an exception if the user already has a profile:
public function store(ProfileRequest $request)
{
$user = Auth::user();
if($user->profiles){
abort(500);
}
$profile = new Profile($request->all());
$user()->profiles()->save($profile);
return 'saved';
}
In your template, you can show a link to either create profile or edit profile depending if the user has one yet.
You could do this check in middleware if you prefer, or make a guard.
Another nice method, as mentioned by #Maraboc in comments, is to create a blank profile on signup, so you only need an edit route.
Worth mentioning, if the user only has one profile, you should name the property 'profile' not 'profiles'
You may create empty profiles when creating new users. Another way is checkout if profile exists in your edit/update actions, like this:
public function edit()
{
//if profile will be edited at first time, then dummy profile will be used
$profile = Auth::user()->profile ?: new Profile();
return view('backend.profile.edit', compact('profile'));
}
public function update(Request $request)
{
//validate your data
//use $fillable in Profile model to whitelist acceptable attributes
if(Auth::user()->profile) {
Auth::user()->profile->update($request->all());
} else {
$profile = new Profile($request->all());
Auth::user()->profile()->save($profile);
}
//redirect to another page
}

Categories