I have mysql tables:
Balance
id | client_id | balance
And
Payments**
id | payment_date | amount | foreign key -> balance_id
What methods are used to update balance when I store payment amount?
Inside your payments model, you can create an event handlers like so:
/**
* The event map for the model.
*
* #var array
*/
protected $dispatchesEvents = [
'created' => \App\Events\PaymentCreated::class,
];
Then you can create an event like so:
PaymentCreatedEvent
<?php
namespace App\Events;
use App\Models\Payments;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
class PaymentCreatedEvent
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $payments;
/**
* Create a new event instance.
*/
public function __construct(Payments $payments)
{
$this->payments = $payments;
}
/**
* Get the channels the event should broadcast on.
*
* #return Channel|array
*/
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
Then you can create a listener to create the balance:
PaymentCreatedListener
<?php
namespace App\Listeners;
use Illuminate\Support\Facades\Mail;
use App\Events\PaymentCreatedEvent;
class PaymentCreatedListener
{
/**
* Create the event listener.
*/
public function __construct()
{
}
/**
* Handle the event.
*
* #param PaymentCreatedEvent $event
*/
public function handle(PaymentCreatedEvent $event)
{
// Insert in to the balance table here
}
}
Then inside your eventserviceprovider.php add
/**
* The event listener mappings for the application.
*
* #var array
*/
protected $listen = [
'App\Events\PaymentCreatedEvent' => [
'App\Listeners\PaymentCreatedListener',
],
];
To listen to your event. You will need to create the insert statement inside the listener... But you get the idea.
In payment issues i think you must handle transaction and check that all inserts/updates run successfully.
You can one of approaches:
1.
In your payment model:
static function boot() {
static::created(function($payment){
//update balance here
}
}
2.
In your controller or other class:
DB::beginTransaction();
try{
//insert payment row
//update balance
DB::commit();
}
catch (\Exception $e){
DB::rollback();
}
Related
How can I enforce uniqueness of a value within overlapping date range in Symfony using Doctrine ORM.
I have the following entity
<?php
/**
* #ORM\Entity
* #ORM\Table("tax_component")
*/
class TaxComponent
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(name="tax_component_id", type="integer")
*/
private ?int $id;
/**
* #ORM\Column(name="tax_component_name", type="string", length=20)
*/
private string $name;
/**
* #ORM\Column(name="tax_component_rate", type="integer")
* #Assert\GreaterThanOrEqual(0)
*/
private int $rate;
/**
* #ORM\Column(name="tax_component_applicable_from", type="datetime_immutable")
*/
private DateTimeInterface $applicableFrom;
/**
* #ORM\Column(name="tax_component_applicable_to", type="datetime_immutable")
*/
public function __construct(string $name, int $rate, ?DateTimeImmutable $applicableFrom = null, ?DateTimeImmutable $applicableTo = null)
{
...
}
}
I want to make $name unique withing overlapping time frames of $applicableFrom and $applicableTo. For example,
$repository->save(
new TaxComponent('inter-state', 1800, new DateTime('2018-04-01:00:00:00'), new DateTime('2019-03-31T23:59:59'))
);
// The following should be allowed since there is no overlap between the two time ranges using the name 'inter-state'
$repository->save(
new TaxComponent('inter-state', 1200, new DateTime('2019-04-01:00:00:00'), new DateTime('2020-03-31T23:59:59'))
);
// The following should fail since 'inter-state' is ambiguous during the period 2019-09-01:00:00:00 to 2020-03-31T23:59:59
$repository->save(
new TaxComponent('inter-state', 1800, new DateTime('2019-09-01:00:00:00'), new DateTime('2020-09-31T23:59:59'))
);
Is there a constraint to enforce this is Symfony?
I am currency planning to check for existing entities from within TaxComponentRepository::save, before calling $this->entityManager->persist. Is there a better solution?
The cleaner way would be to create your own custom assert.
Starting by creating your constraint :
<?php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* #Annotation
*/
class TaxComponentConstraint extends Constraint
{
public $message = 'Another tax component overlap this one: {{ taxComponent}}';
public function getTargets()
{
return self::CLASS_CONSTRAINT;
}
public function validatedBy()
{
return 'App\Validator\Constraints\TaxComponentValidator';
}
}
And now you have to create a validator that will check if there exist an overlap with two tax component.
<?php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class TaxComponentValidator extends ConstraintValidator
{
public function validate($taxComponentObject, Constraint $constraint)
{
//Check however you want if the tax component can be created (So no overlap between two existing TaxComponent)
if($overlappingTaxComponent){
$this->context->buildViolation($constraint->message)
->setParameter('{{ taxComponent }}', $overlappingTaxComponent->__toString())
->addViolation();
}
}
}
Here, $overlappingTaxComponent is a TaxComponent preventing us from making one because of your constraint.
If the constraint is properly done, you can now use it easily in your entity so that it check automatically when submitting the form :
<?php
//...
use App\Validator\Constraints as CustomAssert;
/**
* #ORM\Entity
* #ORM\Table("tax_component")
* #CustomAssert\TaxComponentConstraint
*/
class TaxComponent
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(name="tax_component_id", type="integer")
*/
private ?int $id;
/**
* #ORM\Column(name="tax_component_name", type="string", length=20)
*/
private string $name;
I am trying to isert data in mysql using laravel, while I am getting the error ErrorException (E_NOTICE)
Trying to get property of non-object, where is the problem I dont know please help me.
my controller code is PublicationController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\publication;
use Auth;
class PublicationController extends Controller
{
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
//
return view('publications');
}
/**
* Show the form for creating a new resource.
*
* #return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
//
publications::create([
'user_id' => Auth::user()->id,
'title' => request('title'),
'status' => request('status'),
'year' => request('research_area')
]);
return 'inserted';
}
/**
* 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)
{
//
}
}
While model code is given publication.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class publication extends Model
{
//
protected $fillable = ['title','status','year'];
}
The code of my route is given.
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController#index')->name('home');
Route::get('education', 'EducationController#index');
Route::post('edu', 'EducationController#store');
Route::get('publications','PublicationController#index');
Route::post('pub','PublicationController#store');
The error is given Class ErrorException (E_NOTICE)
Trying to get property of non-object please help if any one know where is the problem
Consider placing PublicationController behind authentication middleware:
class PublicationController extends Controller
{
...
public function __construct()
{
$this->middleware('auth');
}
...
}
You can also use route groups:
Route::middleware(['auth'])->group(function () {
// your routes
});
If Auth::user() is null then Auth::user()->id will give you the exception you mentioned. Placing the routes or controller behind the middleware should solve this.
Edit
This assumes you are using Laravel 5.6 https://laravel.com/docs/5.6. This should work for 5.5 and 5.7.
Finally I found the answer of my question by just including 'user_id' in my model fillable arry and the above code works properly.
İ think you are not logged in so you get error when you try to get Auth::user()-id
Add this contractor to your class i think it should work for you
public function __construct(){
$this->middleware('auth');
}
I have 3 tables : Profile - Permission - ProfilePermissionValue
Profile and Permission are classic entities, and ProfilePermissionValue is an association of a Profile.id, Permission.id, and an extra field representing the value of the permission for the profile.
When I add a Permission, I want a new row being inserted in ProfilePermissionValue for each Profile.
Same on reverse, when I add a new Profile, ... And same on delete by the way.
The question : Is there a way to do it with Doctrine (Symfony 3) functionalities, or I need to code it myself ?
I think you look at the permission <-> profile more strictly than you should. Basically in almost every ACL I worked with there was a assumption - when something is not allowed, it`s disallowed (or when something is not disallowed is allowed which is more dangerous). Which significantly reduce amount of data, you must save.
So when you create your entities like this
<?php
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
*/
class Permission
{
// id column
/**
* #ORM\Column(type="string")
* #var string
*/
private $name;
/**
* #return string
*/
public function getName()
{
return $this->name;
}
}
and
<?php
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity()
*/
class User
{
// id column
// name column
/**
* #ORM\ManyToMany(targetEntity=Permission::class)
* #ORM\JoinTable(name="allowed_permissions",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="permission_id", referencedColumnName="id")}
* )
* #var Permission[]|Collection
*/
private $allowedPermissions;
/**
* #return Permission[]
*/
public function getAllowedPermissions()
{
return $this->allowedPermissions->toArray();
}
}
you can simply implement your own class for interface AuthorizationCheckerInterface as
<?php
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
class Authorizator implements AuthorizationCheckerInterface
{
/**
* #param string $name
* #param User $user
* #return bool
*/
public function isGranted($name, $user)
{
foreach ($user->getAllowedPermissions() as $permission) {
if ($permission->getName() === $name) {
return TRUE;
}
}
return FALSE;
}
}
without any needs of having deny permission in your database.
I have 2 entities: Submission and Vote.
Submission Entity:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Votes", mappedBy="submission",cascade={"persist", "remove" })
* #ORM\JoinColumn(name="id", referencedColumnName="submission_id")
*/
protected $vote;
/**
* #return mixed
*/
public function getVote()
{
return $this->vote->toArray();
}
/**
* #param Votes $vote
* #return $this
*
*/
public function setVote(Votes $vote)
{
if (!$this->vote->contains($vote)) {
$this->vote->add($vote);
}
return $this;
}
Vote entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Submission", inversedBy="id")
* #ORM\JoinColumn(name="submission_id", referencedColumnName="id", nullable=true)
*/
protected $submission;
/**
* #return mixed
*/
public function getSubmission()
{
return $this->submission;
}
/**
* #param mixed $submission
*/
public function setSubmission($submission)
{
$this->submission = $submission;
}
Problem is that when i'm setting vote for previously selected submission:
$submission = $em->getRepository('AppBundle:Submission')->findOneById($submissionId);
$vote = new Votes();
$vote->setId($submission->getId());
$vote->setFeedback($judgeComment);
$vote->setScore($judgeScore);
$vote->setSubmission($submissionId);
$submission->setVote($vote);
$em->persist($submission);
$em->flush();
Vote table
submission_id column is always NULL - i'm not sure what i'm doing wrong. I want to store here submission id, for what this vote is.
You don't have to set ids in doctrine but objects, as it is an ORM
$submission = $em->getRepository('AppBundle:Submission')->findOneById($submissionId);
$vote = new Votes();
$vote->setId($submission->getId()); // <--- Are you sure that you need this?
$vote->setFeedback($judgeComment);
$vote->setScore($judgeScore);
// $vote->setSubmission($submissionId); <--- ERROR!
$vote->setSubmission($submission) // <--- Check advice below
$submission->setVote($vote);
$em->persist($submission);
$em->flush();
If I can also leave a suggestion here, modify setVote as follows
public function setVote(Votes $vote)
{
if (!$this->vote->contains($vote)) {
$this->vote->add($vote);
$vote->setSubmission($this);
}
return $this;
}
and you'll never need to set explicitly both sides of association anymore
I'm also noticing that your class name is plural (should be singular into domain) and that vote attribute into submission is singular (should be plural as it's a collection; you have a -to-Many annotation. Even the setter should be an adder as you're not setting but adding a Vote)
Thank you very much Gerry. Thanks to you problem is solved. Here is the answer:
Submission Entity:
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Votes", mappedBy="submission",cascade={"persist", "remove" })
* #ORM\JoinColumn(name="id", referencedColumnName="submission_id")
*/
protected $vote;
/**
* #return mixed
*/
public function getVote()
{
return $this->vote->toArray();
}
/**
* #param Votes $vote
* #return $this
*
*/
public function setVote(Votes $vote)
{
$this->vote[] = $vote;
$vote->setSubmission($this);
return $this;
}
Vote Entity:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Submission", inversedBy="vote")
* #ORM\JoinColumn(name="submission_id", referencedColumnName="id",onDelete="cascade", nullable=true)
*/
protected $submission;
/**
* #return mixed
*/
public function getSubmission()
{
return $this->submission;
}
/**
* #param mixed $submission
*/
public function setSubmission($submission)
{
$this->submission = $submission;
}
Now when i'm adding new vote, everything is fine:
$vote = new Votes();
$vote->setFeedback($judgeComment);
$vote->setScore($judgeScore);
$submission->setVote($vote);
$em->persist($submission);
$em->flush();
result
How to access event manager in controller constructor ? when I call event manager in constructor , this error appears :
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for event
You don't have access to the service manager at this point, as it's injected once the object has been instantiated.
You could always move you code to be triggered onDispatch() rather than in the contructor:
/**
* Execute the request
*
* #param MvcEvent $e
* #return mixed
* #throws Exception\DomainException
*/
public function onDispatch(MvcEvent $e)
{
// do something here
// or you could use the events system to attach to the onDispatch event
// rather than putting your code directly into the controller, which would be
// a better option
return parent::onDispatch($e);
}
I would just use Events to attach what ever you need, rather than using the controller
Module.php
/**
* Initialize
*
* #param \Mis\ModuleManager
*/
public function init(ModuleManager $manager)
{
$events = $manager->getEventManager();
$sharedEvents = $events->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) {
/* #var $e \Zend\Mvc\MvcEvent */
// fired when an ActionController under the namespace is dispatched.
$controller = $e->getTarget();
$routeMatch = $e->getRouteMatch();
/* #var $routeMatch \Zend\Mvc\Router\RouteMatch */
$routeName = $routeMatch->getMatchedRouteName();
// Attach a method here to do what you need
}, 100);
}