So maybe this TDD stuff is going places…

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.