Track data changes on tables using doctrine - mysql

So the situation is that I am using Doctrine as the ORM for one of my projects.
Now I want to be able to track the changes happening on certain tables of my website without having to much extra coding for that.
For eg. I have a database which has many tables. out of that i have a table users on which I want to track the changes done
1. users has column name with value 'Raman'
2. Using update sql below i modify the row
update users set name = 'Raman Joshi' where name='Raman'
Is there any in built feature in doctrine that allows to create a log table tracking all the data level changes log that was done?

You can use a Doctrine preUpdate event listener to do this. Here's a simple example that will send changes to a logger:
use Psr\Log\LoggerInterface as Logger;
use Doctrine\ORM\Event\PreUpdateEventArgs;
class ChangeLoggerListener
{
protected $logger;
public function __construct(Logger $logger)
{
$this->logger = $logger;
}
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
//find out class and id of object being updated
$obj=$eventArgs->getEntity();
$class=get_class($eventArgs->getEntity());
$id=$obj->getId();
$log="$class($id) updated: ";
//find out what has changed...
$changes=$eventArgs->getEntityChangeSet();
$separator='';
foreach ($changes as $field => $values) {
$log.=$separator."$field changed from {$values[0]} to {$values[1]}";
$separator=", ";
}
//send it to logger
$this->logger->info($log);
}
}
The manual page shows how to register the listener, but if you're using Symfony, you can register the listener as a service with this in your services.yml
my.change_logger:
class: My\ExampleBundle\Listener\ChangeLoggerListener
arguments: [#logger]
tags:
- { name: doctrine.event_listener, event: preUpdate }

Related

Symfony 3.4 : how to log the history of user actions?

I want to store in my database all the user actions done about an entity.
For example, for 1 entity, I want to store :
Created by (= author)
Updated by
Date of creation
Date of update
I want to store the history of the actions of a user, not the last ones. I thought I could create a table with these columns :
log_id
user_id
entity_id
action (= "create" or "update" or something else)
date
And then, I could easily get the last update of my entity and display the date and the user who did it.
Is there a Symfony bundle to do this ? Should I use Monolog ?
I will do this for many entities and I'm not sure if this is the correct way to do...
Is it possible to create only one logs table to store each log about each entity ? It bothers me to create 1 logs table per entity.
Since Doctrine is event based, it's easy:
Either use an extension, like Gedmo Loggable
Or hook into Doctrine's events and log, using Monolog, everything that happens in your app.
Personally I would prefer option 2 since I'm a control maniac, it's a little more complex though. Personally I would also use Monolog so I could abstract away the way how and where the log entries are stored.
When you decide how to approach this and you will need any assistance along the way, please ask another question.
Good luck.
I don't know if that would fit what you need, but you could easily add a Listener to the symfony kernel to log every controller used.
Something like this :
class UserLogListener {
protected $authChecker;
protected $tokenStorage;
protected $entityManager;
public function __construct(TokenStorageInterface $tokenStorage, AuthorizationChecker $authChecker, EntityManager $entityManager)
{
$this->authChecker = $authChecker;
$this->tokenStorage = $tokenStorage;
$this->entityManager = $entityManager;
}
public function onKernelRequest(GetResponseEvent $event)
{
if( $this->tokenStorage->getToken() != null){
$user = $this->tokenStorage->getToken()->getUser();
$currentDate = new \Datetime();
$action = $event->getRequest()->attributes->get('_controller');
$method = $event->getRequest()->getMethod();
$userIp = $event->getRequest()->getClientIp();
$userLogRepository = $this->entityManager->getRepository(UserLog::class);
if($user instanceof User){
$userLog = new UserLog();
$userLog->setUser($user);
$userLog->setIp($userIp);
$userLog->setAction($action);
$userLog->setMethode($method);
$userLog->setDate($currentDate);
if($event->getRequest()->request && $methode=='POST'){
$userLog->setData(json_encode($event->getRequest()->request->all()));
}else{
$userLog->setData($event->getRequest()->getPathInfo());
}
$this->entityManager->persist($userLog);
$this->entityManager->flush();
}
}
}
}
What it does is add to the database (with an entity called UserLog) information about every page called. So you can know which action is made by knowing which controller is called, and you can also log the request data so you can find out what modification/creation the user did.

Expanding this method to write to the database

Hi I followed a tutorial to implement a friend system. It all works find, but I need to post other columns to the row that just the id's. How would I expand that.
This is the method that is accessed when the add friend button is clicked
public function getAdd($id){
$user = User::where('id', $id)->first();
//After passing all checks. Add other account
Auth::user()->addFriend($user);
echo "Sent";
}
AddTenancy Method
public function addFriend(User $user){
$this->friendsOf()->attach($user->id);
}
I assume the relationship is many-to-many between users. And you need to add additional data to the pivot.
Here's how you'd do that:
public function addFriend(User $user){
$this->friendsOf()->attach($user->id, ['another_col' => 'some data']);
}
Replace 'another_col' and some data with your column and your data. You can also add more than 1 column into the array.

Laravel 5.3 Trigger

is there a way to make a laravel trigger for this sql command? sorry I am new to laravel and having a hard time figuring it out.
So what should happen is when the table.a get its content, it will automactically fill the column with ids from the other table. is it possible for laravel? Thank you so much.
UPDATE table_a
INNER JOIN table.b ON table_a.account_code =
table.ac_code
SET table_a.ut_id = table.ut_id, table_a.pj_id
= table.pj_id
I used to use laravel event for trigger stuff in laravel (it assumes you have models on this). In your to be listened Model_B (table_b), you can define something like :
public function boot()
{
parent::boot();
static::created(function ($model) {
/*update table a here*/
$ut_id = $model->ut_id;
$pj_id = $model->pj_id;
Table_A::customUpdate($ut_id, $pj_id);
});
}
Please define your public customUpdate( ) as you want.
Check laravel doc above to see other possibilities for boot methods: creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored

Insert a field with value whenever new instance of modal is created

I want to insert a field with value whenever new row is created for a modal.
Ex: Suppose this is my user.php modal
class User extends Authenticatable
{
protected $guarded = ['id'];
}
What i want is in my application anywhere when i insert a row in user table, then i want to insert an extra column code with its value in user table.
Ex: If i do below in my application
User::create(['name'=>'xyz', 'password' => 'kajsndjk']);
then it should insert an extra column code =>'Qwedf' also in my table.
In my application there are many places where i am creating the users, so i don't want to remember every time to insert code column.
Please suggest how can i achieve it.
Overriding the static create function on the User class is the only thing that will work in my opinion.
public static function create(array $attributes = [])
{
$object = parent::create($attributes);
$object->code = 'some text';
$object->save();
return $object;
}
I've tested and like I expected, oseintow's answer will not work, because it would work only if you directly modified code variable, which you obviously are not doing.
Add this mutator to your User model
public function setCodeAttribute($value)
{
$this->attributes['code'] = "Qwedf";
}
Anytime you are saving a record code will be assigned the Qwedf value

Add WHERE condition to all SQL requests in Laravel

I'm creating an online tool for companies that each have a set of users in Laravel.
When a user is connected, he has a $connected_company_id variable
For every SELECT request (called by ::all(), find(), ...), i would like to add the condition: where company_id = $connected_company_id. I have found this post: laravel set an automatic where clause, but it doesn't work by overriding newQuery().
For every INSERT request, i would like to add the company_id.
Is this possible without changing my code inside all the controllers ?
I thought about extending Eloquent with customEloquent, and then make my models extend customEloquent, but I don't know how to write the code for customEloquent and if it could work.
Well, you could make use of the Eloquent Model Events. I assume you have the connected_company_id stored in the Session company_id
class BaseModel extends Eloquent{
public static function boot(){
parent::boot();
//Column to inject when inserting
static::creating(function ($obj){
$obj->company_id = Session::get('company_id');
});
//Column to inject when updating
static::updating(function ($obj){
$obj->company_id = Session::get('company_id');
});
}
}
You can extend the BaseModel class on all the models that you want the company_id to be inserted or updated. Take a look at Eloquent Model Events for more information.
The above code will automatically insert or update the company_id to the model that you extend the BaseModel to. When you do a Model::all() or Model::get(), you automatically get the company_id on that Model and you can also perform searches as you requested on Point `
Hope this helps.
well, you can just add the company id to the find query.
Model::where("company_id","=",$company_id):
Or you can create a scope:
class theModel extends Eloquent {
static $company_id;
static for_company($company_id){
self::company_id=$company_id;
return __CLASS__;
}
public function scopeCompany($query)
{
return $query->where('company_id', '=', self::company_id);
}
}
//And later
$scope=theModel::for_company($company_id);
$res=$scope::company->where(...);
Disclaimer: I haven't tried this. Just a solution I constructed. Let me know if this works. This will not work under PHP 5.3