cause I sure has hell just made a kickass logging trait. Alone I don’t think I could have wrote this straight up as quickly as I did with TDD. Plus, now it is testable for the life of the project. Why the hell haven’t I been doing this all along?!
Here’s a bit of code fo yo ass. Here’s the migration for the logs table:
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateLogsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('logs', function (Blueprint $table) {
$table->uuid('id');
$table->string('name');
$table->uuid('loggable_id')->nullable();
$table->string('loggable_type')->nullable();
$table->text('body')->nullable();
$table->timestamps();
$table->primary('id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('logs');
}
}
And here the model. Nothing special.
<?php namespace App;
class Log extends Base
{
protected $table = 'logs';
protected $fillable = ['name', 'loggable_id', 'loggable_type', 'body'];
public function loggable()
{
return $this->morphTo();
}
}
Here is the trait that evolved thanks to TDD
<?php namespace App\Flux\Components;
use App\Log;
use Carbon\Carbon;
trait LoggableTrait
{
/**
* Creating the model relationships to Logs
*
* @return mixed
*/
public function logs()
{
return $this->morphMany(Log::class, 'loggable');
}
/**
* Log the passed in message by appending to the current log or create a new log entry.
*
* @param $message
* @return $this
*/
public function log($message)
{
$name = $this->currentLogName();
$log = $this->logs()->firstOrNew(['name' => $name]);
$log->body .= $this->messagePrefix($log->body) . $message;
$log->save();
return $this;
}
/**
* Return the current log
*
* @return mixed
*/
public function currentLog()
{
$name = $this->currentLogName();
return $this->logs()->where('name', $name)->first();
}
public function clearLogs()
{
return $this->logs()->delete();
}
/**
* Return the current log's name build from the current date and model class name.
*
* @return string
*/
protected function currentLogName()
{
return Carbon::now()->format('Y-m-d') . '_' . strtolower(get_class($this));
}
/**
* Returns the prefix for the message being added to the log
* @param $currentLogBody
* @return string
*/
protected function messagePrefix($currentLogBody)
{
return ($currentLogBody ? "\n" : "") . $this->logDateTimeStamp();
}
/**
* The prefixed date time stamp that is added before a log message
*
* @return string
*/
protected function logDateTimeStamp()
{
return Carbon::now()->toDateTimeString() . ' ';
}
}
And here is the implementation of that trait
<?php namespace App;
use App\Flux\Components\LoggableTrait;
class Cluster extends Base
{
use LoggableTrait;
protected $table = 'clusters';
protected $fillable = ['name', 'active', 'status_code'];
}
Pretty neat, huh? Here is the test class that brought this Logging feature about.
<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ClusterLogTest extends TestCase
{
use DatabaseTransactions;
/** @test */
public function a_cluster_can_create_a_new_log_entry()
{
$logString = "some text here";
$cluster = factory(App\Cluster::class)->create();
$cluster->log($logString);
$log = $cluster->currentLog();
$this->assertStringStartsNotWith("\n", $log->body);
$this->assertStringEndsWith($logString, $log->body);
}
/** @test */
public function a_cluster_can_add_to_an_existing_log()
{
$logString = "some text here";
$logString2 = "some text here2";
$cluster = factory(App\Cluster::class)->create();
$cluster->log($logString);
$cluster->log($logString2);
$log = $cluster->currentLog();
$this->assertStringEndsWith($logString2, $log->body);
}
/** @test */
public function a_cluster_can_remove_all_logs()
{
$logString = "some text here";
$logString2 = "some text here2";
$logString3 = "some text here3";
$cluster = factory(App\Cluster::class)->create();
$cluster->log($logString);
$cluster->log($logString2);
$cluster->log($logString3);
$cluster->clearLogs();
$log = $cluster->currentLog();
$this->assertEmpty($log);
}
}
Am I doing this right? I have no idea. What I do know is that I started out needing the ability to log and now I have a trait I can slap on any model to store logs on. Back in the day I would have extended a class to take on this functionality but this is so much better.
Imma keep at it. This was a great start.