YII2 Session saving multiple record on one request - yii2

i'm trying to use Dbsession to track user' activity and i got everything set and running according to yii documentation, but when a user load a page multiple session record was saved in the database in one request. image below shows the data in the database what is the cause of this and any solution to fix this?
In my config file i have this
'session' => [
// this is the name of the session cookie used for login on the frontend
//'name' => 'advanced-frontend',
'class' => 'yii\web\DbSession',
'writeCallback' => function ($session) {
return [
'user_id' => \Yii::$app->user->id,
'ip' => \Yii::$app->clientip->get_ip_address(),
];
},
],

First column (id) is primary key and should be unique (it is declared in this way in migration). You have probably messed something with table schema - you should not be able to save 3 records with the same ID. DbSession is using upsert() and relies on uniqueness of id column.
Make sure that id column is primary key, or at least have UNIQUE constraint.

Related

ExpressJS / MYSQL / Prisma - Save DB' entities changes

I'm looking for a way to save database entities changes for some entities. I mean I need to save in a database table all changes that are done on some tables (add, modify / delete) with ability to track user which did the change.
I'm working on NextJS with a custom ExpressJS server and MYSQL database were I use Prisma as ORM. I think it's maybe possible to write an ExpressJS middleware but I have yet no idea how to do it and asking myself if any library already exist.
Usually I work on PHP Symfony and used to manage this StofDoctrineExtensionsBundle which is great and works as expected. But my current project is a Typescript project only with Express/NextJS/React/Prisma/MYSQL.
Any feedback from your knowledge will be very appreciate.
Thank's in advance.
Regards,
Gulivert
EDIT: My current API which has to be moved to Express/NextJS is still running on Symfony and the table where all changes is logged looks like this :
{
"id": 59807,
"user": "ccba6ad2-0ae8-11ec-813f-0242c0a84005",
"patient": "84c3ef66-548a-11ea-8425-0242ac140002",
"action": "update",
"logged_at": "2021-11-02 17:55:09",
"object_id": "84c3ef66-548a-11ea-8425-0242ac140002",
"object_class": "App\\Entity\\Patient",
"version": 5,
"data": "a:2:{s:10:\"birth_name\";s:2:\"--\";s:10:\"profession\";s:2:\"--\";}",
"username": "johndoe",
"object_name": "patient",
"description": null
}
Explanation about database columns:
user => relation to user table
patient => relation to patient table
action => can be "create"/"update"/delete"
logged_at => date time where the change was done
object_id => entity row ID where an entity get a change
object_class => the entity updated
version => how many time the object was change
data => all data changed during the modification
username => the username of logged user did the change
object_name => a string to identify the object modified without
using the namespace of object_class
description => a value that can be update on some specific change * during usually the action delete to keep a trace what was deleted for instance
You might find prisma middleware useful for this.
Check out the example with session data middleware which is somewhat similar to what you're doing.
For your use-case the middleware might look like something like this:
const prisma = new PrismaClient()
const contextLanguage = 'en-us' // Session state
prisma.$use(async (params, next) => {
// you can find all possible params.action values in the `PrismaAction` type in `.prisma/client/index.d.ts`.
if (params.model == '_modelWhereChangeIsTracked_' && (params.action == 'create' || params.action == "update")) {
// business logic to create an entry into the change logging table using session data of the user.
}
return next(params)
})
// this will trigger the middleware
const create = await prisma._modelWhereChangeIsTracked_.create({
data: {
foo: "bar"
},
})
However, do note that there are some performance considerations when using Prisma middleware.
You can also create express middleware for the routes where you anticipate changes that need to be logged in the change table. Personally, I would prefer this approach in most cases, especially if the number of API routes where changes need to be logged is known in advance and limited in number.

Laravel Eloquent create method skipping IDs and returning them

I'm simply trying to create a new model through Eloquent's create method. However, we're seeing that the new model will sometimes contain an id that has been skipped in mysql. This id is designated as an autoincrement column in mysql.
We are also seeing this happen when using DB:getPdo()->lastInsertId() immediately after the model has been created.
$questionResponse = $response->questionResponses()->create([
'survey_question_id' => $question->id,
'response' => 5
]);
$response_id = $questionResponse->id; // weve also used DB::getPdo()->lastInsertId();
Queue::push('TheWorker', array('survey_id' => $survey->id, 'response_id' => $response_id, 'question_id' => $question->id, 'user_id' => $user->id, 'student_target_id' => $student_target_id));
TheWorker then fails due to receiving an incorrect $repsonse_id that doesn't exist in the DB.
We would expect that the newly created model would contain the id that matches the record created in mysql.
Thanks in advance!

Puppeteer: Shoutbox/MessageBoard logging

I'm scraping a shoutbox which is limited to 10 messages; it's asynchronous and when the 11th item appears the first one is gone.
I set up a puppeteer, it scrapes the structure correctly as an array, which I dump to mongodb. The easiest way automating this I came up with is running script with the watch command and static interval.
The question is how to skip duplicates items in log, items shouldn't be unique, just don't dump the same twice. And there's probably a better way to cycle this process.attached screenshot
You can use db.collection.distinct() in MongoDB to obtain the distinct messages from your database:
db.messages.distinct( 'message' );
Alternatively, you can use db.collection.createIndex() to create a unique index in your database so that the collection will not accept insertion or update of a document where the index key value matches an existing value in the index:
db.messages.createIndex( { 'message' : 1 }, { 'unique' : true } );
In your Puppeteer script, you can use page.evaluate() in conjunction with the Set object to obtain distinct messages from the web page that you are scraping:
const distinct_messages = await page.evaluate( () => new Set( Array.from( document.querySelectorAll( '.message' ), e => e.textContent ) ) );

Checking 2 Column for Duplicate

Currently, I have a system to hold main data
1) The email
2) The owner(user_id)
Every time someone uploads , I need to make sure that it doesn't not exist in the system. The catch is as I upload more and more, the amount of time taken to check for duplicate will grow steeply, just like the graph as shown.
Question
1) How do i check for duplicate efficiently?
2) I indexed the user_id and the email should I Fulltext it? I wont be reading the text but will be searching for it as a whole, so index is more logical?
3) I also read about creating Hash combining email&owner id then index the hash. Will it be a big difference from the current method?
4) Last method i thought of was to create a primary key for both email and user_id , once again idk how the performance would turn out.
Please advice.
Code
$exist = DB::table('contact')->where('email', $row['email'])->where('user_id', $user_id)->count();
if($exist < 1){
DB::table('contact')->insert(
['email' => $row['email'], 'name' => $row['name'], 'user_id' => $user_id]
);
}
Use Laravel Validator:
public function store(Request $request)
{
$this->validate($request, [
'user_id' => 'required|unique',
'email' => 'required|unique',
]);
//some logic here
}
Also you should use unique constraint in your database.

Magento add a column to sales_flat_quote table and add data

I am coming from a previous enviornment where doing things like modifying queries and adding columns was just a matter of writing the sql and executing it. However, now that I'm working in Magento I want to do things "the Magento way".
Scenario: we use paypal express, and before the controller redirects to paypal, I would really like to add a field (if not there already) in sales_flat_quote, called paypal_status - and set the value = 1 (we'll call it, sent to paypal).
On return I want to update that to either 2 or 3 (returned and pending transaction, or returned and captured transaction).
So there are two things I need to know how to do:
have something like $db->addColumn('paypal_status') where it will only add if not exists, and
write UPDATE sales_flat_quote SET paypal_status = 1 WHERE entity_id =
{whatever}
This will be inside the ...Paypal_Express class.
Open database and fire this SQL: Alter table sales_flat_quote Add paypal_status tinyint(1) NOT NULL DEFAULT 1;
Alternatively, you can write following in your SQL file (located at CompanyName\MyModuleName\sql\companyname_modulename_setup) of your custom module. This file will get executed only one time , that is the first time when the module is installed. At that time your custom column will not be there in database so it will create one.
$installer = $this;
$installer->startSetup();
$installer->run("ALTER TABLE `{$installer->getTable('sales/quote')}` ADD `paypal_status` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'My Custom Paypal Status';");
$installer->endSetup();
Clear all cahces.
To save data :
$myValue = 2;
Mage::getSingleton("checkout/cart")->getQuote()->setPaypalStatus($myValue)->save();
Mage::getSingleton("checkout/cart")->getQuote() will give you current quote.
In your sql file at CompanyName\MyModuleName\sql\companyname_modulename_setup copy the following code in order to create the column.
$installer = $this;
$installer->startSetup();
$installer->getConnection()
->addColumn($installer->getTable('sales/quote'),
'paypal_status',
array(
'type' => Varien_Db_Ddl_Table::TYPE_INTEGER,
'nullable' => true,
'comment' => 'Paypal Status',
)
);
$installer->endSetup();
Logout and login, and flush magento cache in order to add the column to the table.
The Express Checkout controller is in app/code/core/Mage/Paypal/Controller/Express/Abstract.php. If you want to add a field before the controller redirects to paypal you can modify the _initCheckout() method like this:
protected function _initCheckout()
$quote = $this->_getQuote();
if (!$quote->hasItems() || $quote->getHasError()) {
$this->getResponse()->setHeader('HTTP/1.1','403 Forbidden');
Mage::throwException(Mage::helper('paypal')->__('Unable to initialize Express Checkout.'));
}
$quote->setPaymentStatus(1); // Here is your change
$this->_checkout = Mage::getSingleton($this->_checkoutType, array(
'config' => $this->_config,
'quote' => $quote,
));
return $this->_checkout;
}