Laravel execute function inside relation - php

I have some troubles after implementing polymorphic relations into my Laravel database.
Namely, I'm trying to execute two queries, one to save some counts and the other to update the polymorphic table. But update never happens, and I have no errors. Actually, update function works, but it's not executing function before that.
So, here is what my function looks like:
public function setThreadLastInteraction(Thread $thread)
{
return $this->ancestorsAndSelf->map(function ($c) use ($thread) {
try {
$c->statistics(function ($query) use ($thread) {
try {
$query->interaction()->save($thread);
} catch (\Exception $e) {
Log::error($e, __NAMESPACE__, __FUNCTION__);
}
})->update([
'threads_count' => DB::raw('threads_count + 1'),
]);
} catch (\Exception $e) {
Log::error($e, __NAMESPACE__, __FUNCTION__);
}
})->toArray();
}
First, I have to update every category related to the other one, after that I'm doing some function for each of them.
As you see, I'm accessing the Statistics model. Only thing that is not working is inside the statistics function:
try {
$query->interaction()->save($thread);
} catch (\Exception $e) {
Log::error($e, __NAMESPACE__, __FUNCTION__);
}
I'm obviously calling relation as it should be, cause It's updating 'threads_count'.
But here is how it looks like:
public function statistics(): HasOne
{
return $this->hasOne(Statistics::class);
}
And finally, inside Statistics class, relation that should be updated but it's not.
public function interaction(): MorphTo
{
return $this->morphTo();
}
I tried to remove everything inside statistics function and do it like (still inside map function):
$c->statistics()->interaction()->save($thread);
$c->statistics()->update([
'threads_count' => DB::raw('threads_count + 1'),
]);
That way, I'm accessing directly to the model. But it says that interaction relationship is not existing.
What could be the issue? Why Interaction function is not accessible within relation?

Related

How to implement Uncle Bob's ideia of zero parameters in this case?

I am trying to apply a principle I read on Uncle Bob's book "Clean Code", which states that the optimal amount of parameters for a method is zero. I found this is very hard to accomplish, but anyways here I am trying it and I'd like your help to understand the best way to come closer to it in my code.
So far, I have a controller and a simple service to do the job I want. My controller is implemented as follows:
<?php
namespace App\Http\Controllers\Cms;
use App\Http\Controllers\Controller;
use App\Http\Requests\GroupRequest;
use App\Models\Group;
use App\Services\GroupsService;
use Illuminate\Http\Request;
class GroupsController extends Controller
{
protected $group_service;
public function __construct(GroupsService $group_service)
{
$this->group_service = $group_service;
}
public function index()
{
$groups = $this->group_service->listAll();
return view('cms.groups.index', compact('groups'));
}
public function store(GroupRequest $request)
{
$result = $this->group_service->createGroupWith($request->all());
return redirect()->back()->with('message', $result['msg']);
}
public function update(GroupRequest $request, Group $group)
{
$result = $this->group_service->updateAGroupWith($request->all(), $group);
return redirect()->back()->with('message', $result['msg']);
}
}
And bellow is my service
<?php
namespace App\Services;
use App\Models\Group;
use Illuminate\Support\Facades\DB;
class GroupsService
{
public function listAll()
{
$groups = Group::all();
return $groups;
}
public function createGroupWith($data)
{
try {
DB::beginTransaction();
$modules_id = array_pop($data);
$group = Group::create($data);
$group->modules()->attach($modules_id);
DB::commit();
return ['msg' => 'Grupo criado com sucesso!'];
} catch (\Throwable $error) {
DB::rollBack();
return ['msg' => $error->getMessage()];
}
}
public function updateAGroupWith($data, $group)
{
try {
DB::beginTransaction();
$modules_ids = array_pop($data);
$group->update($data);
$group->modules()->sync($modules_ids);
DB::commit();
return ['msg' => 'O grupo foi atualizado com sucesso!'];
} catch (\Throwable $error) {
DB::rollBack();
return ['msg' => $error->getMessage()];
}
}
}
As you can see, my service is instanciated by Laravel on the controller's __construct method and is used on the rest of my methods. The problem with this is that when storing or updating a register, I need to pass the data and (for the update) the gruop I want to update. This violates Uncle Bob's ideal of zero parameters and thus my question: is there a way to solve this?
I have come up with some ideas. The first one would be to continue instanciating the service as it is already done, but having properties such as group and data defined before calling the method I want. Something as implemented bellow. In this case the problem is that if I want to be really idealistic, I don't if I can keep accessing properties from an object and changing them, I believe I'd need to instanciate the object with it's state already defined anyways.
public function update(GroupRequest $request, Group $group)
{
$this->group_service->group = $group;
$this->group_service->data = $request->all();
$result = $this->group_service->updateAGroup();
return redirect()->back()->with('message', $result['msg']);
}
The second ideia would be to instanciate the service as I need it on each method:
public function update(GroupRequest $request, Group $group)
{
$service = new GroupService($data, $group);
$result = $service->updateAGroup();
return redirect()->back()->with('message', $result['msg']);
}
The only problem with the idea above is that in the fist method, the one for storing, I'd need to make the $group parameter for the $service = new GroupService($data, $group); statement optional. But I don't know if that's really a problem or a whim of mine.
And there's a third idea, but it seems way too much. It would be to make have a service per case (storing, updating and deleting maybe) and then I wouldn't have to worry with having optional parameters on the __construct method.
Anyways, I'd like to know your ideas, comments, and critiques too. Please take into consideration that I know this is very idealistic, but I'd really like to know how to come closer to it using with the patterns already defined on Laravel.
Thanks in advance.

Laravel - catching a 404

I have this update function I'm aware the findOrFail call returns a model not found exception sent to the user. Is there a way you can add some code to catch this if an ID is not found or does the call do that for me? Here is the function it needs applying too.
public function update(string $id)
{
$this->user = Auth::user();
$this->film = Film::findOrFail($id);
if (!$this->hasFilm()) {
abort(400, "You don't have a film configured");
}
$this->validation();
$this->transaction();
return $this->film->toJson();
}
You can manually do a try {...} catch {...} in that function.
If you want to do that for all the places that will throw ModelNotFoundException, you can look at app/Exceptions/Handler.php and change the render method
However, if you see you're doing this for many other exceptions, it's better if you create your own exceptions, as documented in the docs
If you want to throw a custom abort when the PodcastProfile is not found, just for this method, you can do something like:
public function update(string $id)
{
$this->user = Auth::user();
$this->podcastProfile = PodcastProfile::find($id);
if (!$this->podcastProfile) {
abort(400, "You don't have a podcast profile configured");
}
$this->validation();
$this->transaction();
return $this->podcastProfile->toJson();
}
If the PodcastProfile is not found it'll be empty, and then you'll get the abort(400)

Why the deleted data still appear in the interface?

Look here :
When I click button delete, data deleted still appear in the interface.
I look at the table in the database, the data is erased.
Data can be lost if I run this: php artisan cache:clear on git bash.
How without running the command, data can be erased?
UPDATE
My controller is like this :
public function deleteAccount(Request $request)
{
$id_account = $request->input('id_account');
try{
if($this->user_service->deleteAccount($id_account)) {
$status='success';
}else {
$status = 'failed';
}
return redirect('member/profile/setting/account')->with('status',$status);
}catch (\Exception $exception){
return $this->respondInternalError();
}
}
My service is like this :
public function deleteAccount($id_account)
{
return $this->user_bank_repository->delete($id_account);
}
My repository is like this :
<?php
namespace App\Repositories;
use App\Models\UsersBank;
use Illuminate\Contracts\Container\Container;
use Rinvex\Repository\Repositories\EloquentRepository;
class UserBankRepository extends EloquentRepository
{
public function __construct(Container $container)
{
$this->setContainer($container)
->setModel(UsersBank::class)
->setRepositoryId('rinvex.repository.uniqueid');
}
public function delete($id)
{
$data = self::find($id)->delete();
return $data;
}
}
I see you are also using the rinvex repository. I don't see your code about fetch data. I assume you are fetching data from the repository cache. In your repository, your delete function.
public function delete($id){
return $this->delete($id);
}
this should clear the repository cache.
if you would like clear the cache of the repository manually, try the following in your repository method.
$this->forgetCache();
or
$this->getContainer('events')->fire($this->getRepositoryId() . '.entity.updated', [$this, $modelInstance]);
actually forgetCache() method shall do, I'm using it in my repository after I made mass update to my table.

Laravel Implict Binding got No query results for model

I want suggestion how to handle and which method is best one. Implicit Binding or Normal Binding method.
I'm using Laravel route implicit binding. when I post wrong ID, I got error No query results for model how to handle in controller not Exception Handler. Now I done with exception handler but need better solution to handle this or avoid Implicit binding.
//Web.php
Route::delete('/master/user/department/{department}/delete', ['as' => 'master.user.department.destroy', 'middleware' => 'permission:master.user.department.destroy', 'uses' => 'Master\User\DepartmentController#destroy']);
//DepartmentContrller.php
public function destroy(Department $department)
{
try {
$department->delete();
return redirect(route('master.user.department.index'))->with('success', array(' Department Deleted successfully'));
} catch (Exception $e) {
return back()->with('criticalError', array($e->getMessage()));
}
}
//Handler.php
if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException)
{
return redirect()->back()->with('custom_modal', ['Model Not Found Exception', $exception->getMessage()]);
}
The below code is perfectly work, I would like to know which method is best one.
//DepartmentContrller.php
public function destroy($id)
{
try {
$department=Department::find($id);
if($department){
$department->delete();
return redirect(route('master.user.department.index'))->with('success', array(' Department Deleted successfully'));
}
else{
return back()->with('criticalError', array('Department is not found.'));
}
} catch (Exception $e) {
return back()->with('criticalError', array($e->getMessage()));
}
}
Both methods are valid. It is up to you to choose which method is appropriate for your situation.
Implicit model binding will let you get code out the door quicker, but you give up some control.
Explicit (normal) binding will take more code to write, but you have complete control over how the exceptions are caught and handled.
Just an FYI, if you stick with implicit binding, the ModelNotFoundException has a getModel() method that will give you the name of the model that caused the exception. This will let you customize your exception handling a little bit more, but still doesn't give you the same control as handling the exception where it happens.
All of above method be work for you but you can override elqoent method --find() in case of you in your respective model
// Put this in any model and use
// Modelname::find($id);
public static function findOrCreate($id)
{
$obj = static::find($id);
return $obj ?: new static;
}
in depth description

Phalcon\Mvc\Model - rolling back failed transaction

The code:
class myModel extends Phalcon\Mvc\Model
{
public function beforeSave()
{
$this->getDi()->getShared('db')->begin();
}
...
public function afterSave()
{
$this->getDi()->getShared('db')->commit();
}
}
My question is - what happens if along the way, between beforeSave() and afterSave() there's an Exception thrown - how can I cleanly roll back the transaction? Where should I stick $this->getDi()->getShared('db')->rollback(); in to?
Thanks!
I'd recommend overloading the save() method entirely.
Here's my example with transactions. Note that you won't need transactions if you're not going to implement additional logic here (like removing related models)
/**
* Delete old relations before saving new ones
*/
public function save($data=null, $whiteList=null)
{
$db = $this->getDI()->get('db');
try
{
// Start transaction
$db->begin();
// ... Do some additional work. Remove related models etc...
// Update model
if (!parent::save($data, $whiteList))
throw new \Exception('Cannot save the model');
$db->commit();
}
catch (\Exception $e)
{
$db->rollback();
$this->appendMessage(new \Phalcon\Mvc\Model\Message($e->getMessage(), '', 'error', $this));
return false;
}
return true;
}

Categories