Spatie activity_log: which method writes to which column? 🐘

If you’re using spatie/laravel-activitylog, you’ve probably written something like activity()->event(…)->log(…) a hundred times without thinking about where each piece lands in the database. The fluent API is friendly, but the column mapping isn’t obvious until you go look — so here it is in one place.

The package writes to a single table called activity_log. Every chained method on the builder corresponds to one column on that row. 💡

1
2
3
4
5
6
7
activity()
    ->useLog('SyncCampaignUsersJob')
    ->event('Sync user without detaching')
    ->performedOn($campaign)
    ->causedBy($actor)
    ->withProperties(['chunk' => '3/20', 'count' => 100])
    ->log('Processing chunk 3/20 (100 users).');

That single fluent call writes one row. Here’s the full mapping:

Method Column(s) What it stores
useLog(“string”) log_name Filterable bucket like “Auth” or “SyncCampaignUsersJob”. Never leave empty — defaults to the literal string “default”, which makes filtering useless.
log(“string”) description Free-form, human-readable message. Returned by $activity->description.
event(“string”) event Short verb-ish label like “created”, “updated”, “Synced user with detaching”. Useful for grouping similar actions.
performedOn($model) subject_type + subject_id Polymorphic reference to the affected model. e.g. “App\Models\Campaign” + 6.
causedBy($user) causer_type + causer_id Polymorphic reference to the actor. Pass a model instance or just the ID.
withProperties([…]) properties Arbitrary JSON. Great for structured context: counts, IDs, batch labels, before/after diffs.

The empty-useLog gotcha 🪤

Here’s the failure mode worth burning into memory. This call:

1
2
3
4
5
activity()
    ->event('Sync user without detaching')
    ->performedOn($campaign)
    ->causedBy($actor)
    ->log('Synced 100 users.');

…silently writes log_name = “default”. Six months later you open the activity log dashboard, filter by log name, and you’re staring at 47,000 rows in the default bucket. Always add useLog() with a meaningful string. The class name of the job or service writing the log is a perfectly fine default — future-you will thank present-you when grep’ing through audit history.

One more nuance: causer_type

If you pass an integer to causedBy(), the package needs to know what model that ID points to. By default it assumes your auth user model (set in config/auth.php). If your causers are sometimes a User and sometimes a SystemActor or a tenant model, pass the model instance instead of the ID — the polymorphic columns will resolve correctly and querying back becomes painless.

That’s the whole mental model: one row, one chained call, one column per method. Keep useLog() populated and you’ll have a queryable audit trail instead of a blob of “default” entries. 🎯

Posted in php | Tagged , , , | Leave a comment

The Null Coalescing Operator: A Small PHP Feature That Quietly Changed Everything

If you’ve been writing PHP for a while, you probably remember the days of nested „isset()” checks cluttering up every template and controller. Since PHP 7, there’s a much cleaner way — and if you haven’t fully embraced it yet, it’s worth a second look.

The null coalescing operator (??) returns the left operand if it exists and isn’t null, otherwise the right. No warnings, no notices, no ceremony.

1
2
3
4
5
6
7
8
9
<?php
// The old way — verbose and easy to get wrong
$username = isset($_GET['user']) ? $_GET['user'] : 'guest';

// With null coalescing — same behavior, far less noise
$username = $_GET['user'] ?? 'guest';

// It chains too, which is where it really shines
$config = $userConfig['theme'] ?? $siteConfig['theme'] ?? 'default';

PHP 7.4 took it a step further with the null coalescing assignment operator (??=), which only assigns if the variable is currently null or unset:

1
2
3
4
5
6
7
8
9
<?php
$options = ['timeout' => 30];

// Only set 'retries' if it isn't already defined
$options['retries'] ??= 3;
$options['timeout'] ??= 60; // stays 30 — already set

print_r($options);
// Array ( [timeout] => 30 [retries] => 3 )

One subtle thing to keep in mind: ?? only reacts to null or unset — not to falsy values like „0″, „””, or „false”. That’s usually what you want, but it’s a meaningful difference from the older ?: (Elvis) operator, which falls back on any falsy value.

1
2
3
4
5
<?php
$count = 0;

echo $count ?? 10;  // prints 0 — because 0 is not null
echo $count ?: 10;  // prints 10 — because 0 is falsy

Small syntax, big quality-of-life improvement. If your codebase still has rows of „isset()” ternaries, refactoring them is one of those low-risk cleanups that pays off every time someone reads the file next. 🐘

Posted in php | Tagged | Leave a comment

Did You Know? Python’s Walrus Operator Can Make Your Code Cleaner

Did you know? Since Python 3.8, you can use the walrus operator ( := ) to assign a value to a variable as part of an expression. It’s a small piece of syntax that can meaningfully tidy up loops and comprehensions where you’d otherwise compute the same value twice.

Here’s a classic example — reading lines from a file until you hit an empty line:

1
2
3
4
5
6
7
8
9
10
11
# Without the walrus operator
with open("data.txt") as f:
line = f.readline()
while line:
print(line.strip())
line = f.readline()

# With the walrus operator — assign and test in one step
with open("data.txt") as f:
while (line := f.readline()):
print(line.strip())

It’s also handy in list comprehensions when you want to filter on a computed value without recomputing it:

1
2
3
4
5
6
7
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# Keep only squares greater than 20, without squaring twice
big_squares = [sq for n in numbers if (sq := n * n) &gt; 20]

print(big_squares)
# [25, 36, 49, 64, 81, 100]

A word of caution: the walrus operator is powerful but easy to overuse. Reach for it when it genuinely removes duplication or makes intent clearer — not just because it’s clever. 🐍

Posted in Python | Tagged | Leave a comment

Did You Know? Python Dictionaries Preserve Insertion Order

Did you know? Since Python 3.7, the built-in

1
dict

type officially preserves the order in which keys are inserted. Before that, if you needed ordering guarantees you had to reach for

1
collections.OrderedDict

. Today, a plain dictionary is enough for most cases.

Here’s a small demonstration:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Keys stay in the order they were added
user = {}
user["name"] = "Ada"
user["role"] = "Author"
user["joined"] = 2026

for key, value in user.items():
    print(f"{key}: {value}")

# Output:
# name: Ada
# role: Author
# joined: 2026

This also means dictionary comprehensions and merges keep a predictable order, which is surprisingly useful when serializing to JSON or building config objects:

1
2
3
4
5
6
7
defaults = {"host": "localhost", "port": 8080}
overrides = {"port": 9090, "debug": True}

# Merge with the | operator (Python 3.9+)
config = defaults | overrides
print(config)
# {'host': 'localhost', 'port': 9090, 'debug': True}

One caveat: ordering is a property of the dictionary, not of equality. Two dicts with the same keys and values are considered equal even if their insertion order differs. 🐍

Posted in Python | Tagged | Leave a comment

List open or listening ports

MacOS

1
lsof -nP -i4TCP

RedHat/Centos7

1
netstat -tulpn
Posted in Bash, Operating System | Leave a comment

MongoDB Notes

When using MongoDB to store files, we have 2 collections:
1. The place where MongoDB store the file metadata: store.files
2. Ans place where MongoDB store the file content: store.chunks

Depending on the size of the file, one entry in store.files can points to many entries in store.chunks. The bigger the file, the more entry you’ll encounter.

1
2
3
4
5
6
7
8
// Show all/ list all entries from store.files
db.getCollection('store.files').find({});

// Show only a particular entry from store.files
db.getCollection('store.files').find({ _id : ObjectId("5b02d232cbce1d07e08401c7")});

// The same can be used for store chunks.
db.getCollection('store.chunks').find({});

The metadata/fields in the store.files can be expanded during query runtime (not saved into the MongoDB, only displayed during runtime)

1
2
3
4
db.getCollection('store.files').aggregate([
    { $match: { _id : ObjectId("5b02d232cbce1d07e08401c7")} },
    { $addFields:{'key_reference':'1234'}}
]);

Or we do an update into store.files which will be saved into the MongoDB

1
db.getCollection('store.files').updateMany({ _id : ObjectId("5b02d232cbce1d07e08401c7")},{$set:{'key_reference':'1234'}});
Posted in Uncategorized | Leave a comment

CentOS 6 repo Settings

To fix repo settings in CentOS 6

1. make sure there is no proxy or funny settings in
vi /etc/yum.conf

2. There are a couple of files within /etc/yum.repos.d/. Make sure the url are correct (accessible) and enabled=1
ll /etc/yum.repos.d/

3. Cleanup the repo, list and retest
yum –enablerepo=base clean metadata;
yum repolist all
yum search java-1.8.0-openjdk

Posted in Linux | Leave a comment

Show Linux Partition Tree Mountpoint and If SSD

1
lsblk -o TYPE,NAME,KNAME,UUID,MOUNTPOINT,SIZE,ROTA
Posted in Linux | Leave a comment

Setting log4j log level programmatically

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static void setupLog4j() {
        System.out.println("setupLog4j");
        BasicConfigurator.resetConfiguration();
        // Start clean.
        Logger.getRootLogger().removeAllAppenders();
        // Create appender
        ConsoleAppender console = new ConsoleAppender();
        // Configure the appender
        String PATTERN = "%d --[ %p ] %l: %m%n";
        console.setLayout(new PatternLayout(PATTERN));
        console.activateOptions();
        console.setName("stdout");
        Logger.getRootLogger().setLevel(Level.DEBUG);
        BasicConfigurator.configure(console);
        LOG = Logger.getLogger(MinerTest.class);
}
Posted in java | Leave a comment

Print java stack trace from anywhere

Need to know which code call a specific location?
Dump the stack trace:

1
LOG.trace(ExceptionUtils.getStackTrace(new Throwable()));
Posted in java | Leave a comment