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

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.

Related

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

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

Track data changes on tables using doctrine

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 }

Codeigniter Noob Question--active record and results?

I'm writing a simple app. I need to block user from a page if their credit is < 0.
I have a table "User_profiles" with a "credit" row.
How can I set up a model in conjunction with the controller to send the user to another page if the value of "credit" is 0?
This should be straightforward, but I'm new at the select->where stuff...
It has to be the row of the current user too--I don't know how to traverse arrays very well yet.
Thanks!
Well, the easiest solution would be to just load a different view...
As for the model, it would look like this:
class UserModel extends Model {
public function getUserCredit($id) {
$this->load->database();
//effectively generates: SELECT `credit` FROM `User_profiles` WHERE `id`=$id
$query = this->db->select('credit')->where('id',$id)->get('User_profiles');
//row() executes the query for a single result, returns the credit property
return $query->row()->credit;
}
}
Then in the controller:
class Users extends Controller {
//....
public function credit() {
$this->load->model('userModel','users');
// assuming the session library has been loaded
$user_id = $this->session->userdata('id');
$credit = $this->users->getUserCredit($user_id);
if ($credit == '0') $this->load->view('users/no_credit');
else $this->load->view('users/credit');
}
}
That's untested, but it should at least help you get the idea.
When you request the page /users/credit/1, CI will call the Users::credit(1) action.
It then loads UserModel as $this->users
You call $this->users->getUserCredit(1), which translates to UserModel::getUserCredit(1), to store as $credit
The model loads the database.
You tell the db to select('credit') (select the credit column), where('id',1) (where the id = 1), then get('User_profiles') (get matching rows from the User_profiles table). That returns a query, which you store as $query for readability.
getUserCredit returns the credit property of the single-row result of the query
If $credit == 0, you load the view views/users/no_credit.php
Otherwise, you load the view views/users/credit.php (it's conventional to name the views after the actions they represent and put them in a folder corresponding to the controller)