Repository: ryangjchandler/orbit (php)

Issue:
Fix `$hidden` columns not being stored on disk
Trying to use Orbit as a flat-file user model doesn't work at the moment because it doesn't store `$hidden` properties in the file.

Pull request description:
fix: `$hidden` properties not being stored on disk
Closes #43.

Patch:
diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml
deleted file mode 100644
index ca33952..0000000
--- a/.github/workflows/psalm.yml
+++ /dev/null
@@ -1,33 +0,0 @@
-name: Psalm
-
-on:
-  push:
-    paths:
-      - '**.php'
-      - 'psalm.xml.dist'
-
-jobs:
-  psalm:
-    name: psalm
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v2
-
-      - name: Setup PHP
-        uses: shivammathur/setup-php@v2
-        with:
-          php-version: '7.4'
-          extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
-          coverage: none
-
-      - name: Cache composer dependencies
-        uses: actions/cache@v2
-        with:
-          path: vendor
-          key: composer-${{ hashFiles('composer.json') }}
-
-      - name: Run composer install
-        run: composer install -n --prefer-dist
-
-      - name: Run psalm
-        run: ./vendor/bin/psalm --output-format=github
diff --git a/src/Concerns/Orbital.php b/src/Concerns/Orbital.php
index e9f0990..fd9681d 100644
--- a/src/Concerns/Orbital.php
+++ b/src/Concerns/Orbital.php
@@ -127,6 +127,7 @@ trait Orbital
         $columns = $schema->getColumnListing($table);
 
         $driver->all(static::getOrbitalPath())
+            ->filter()
             ->map(function ($row) use ($columns) {
                 return collect($row)
                     ->filter(fn ($_, $key) => in_array($key, $columns))
diff --git a/src/Drivers/FileDriver.php b/src/Drivers/FileDriver.php
index 05a0502..b95d8e0 100644
--- a/src/Drivers/FileDriver.php
+++ b/src/Drivers/FileDriver.php
@@ -67,6 +67,13 @@ abstract class FileDriver implements DriverContract
         return $directory . DIRECTORY_SEPARATOR . $key . '.' . $this->extension();
     }
 
+    protected function getModelAttributes(Model $model)
+    {
+        return collect($model->getAttributes())
+            ->map(fn ($_, $key) => $model->{$key})
+            ->toArray();
+    }
+
     abstract protected function extension(): string;
 
     abstract protected function dumpContent(Model $model): string;
diff --git a/src/Drivers/Json.php b/src/Drivers/Json.php
index fe82c46..cd16e21 100644
--- a/src/Drivers/Json.php
+++ b/src/Drivers/Json.php
@@ -9,14 +9,20 @@ class Json extends FileDriver
 {
     protected function dumpContent(Model $model): string
     {
-        $data = array_filter($model->attributesToArray());
+        $data = array_filter($this->getModelAttributes($model));
 
         return json_encode($data, JSON_PRETTY_PRINT);
     }
 
     protected function parseContent(SplFileInfo $file): array
     {
-        return json_decode(file_get_contents($file->getPathname()), true);
+        $contents = file_get_contents($file->getPathname());
+
+        if (! $contents) {
+            return [];
+        }
+
+        return json_decode($contents, true);
     }
 
     protected function extension(): string
diff --git a/src/Drivers/Markdown.php b/src/Drivers/Markdown.php
index 5ad3de3..15d3683 100644
--- a/src/Drivers/Markdown.php
+++ b/src/Drivers/Markdown.php
@@ -14,7 +14,7 @@ class Markdown extends FileDriver
 
     protected function dumpContent(Model $model): string
     {
-        $matter = array_filter($model->attributesToArray(), function ($value, $key) {
+        $matter = array_filter($this->getModelAttributes($model), function ($value, $key) {
             return $key !== 'content' && $value !== null;
         }, ARRAY_FILTER_USE_BOTH);
 
diff --git a/src/Drivers/MarkdownJson.php b/src/Drivers/MarkdownJson.php
index 0bfcd68..1c85079 100644
--- a/src/Drivers/MarkdownJson.php
+++ b/src/Drivers/MarkdownJson.php
@@ -10,7 +10,7 @@ class MarkdownJson extends Markdown
 {
     protected function dumpContent(Model $model): string
     {
-        $matter = array_filter($model->attributesToArray(), function ($value, $key) {
+        $matter = array_filter($this->getModelAttributes($model), function ($value, $key) {
             return $key !== 'content' && $value !== null;
         }, ARRAY_FILTER_USE_BOTH);
 
diff --git a/src/Drivers/Yaml.php b/src/Drivers/Yaml.php
index 6d2bda7..c8b9893 100644
--- a/src/Drivers/Yaml.php
+++ b/src/Drivers/Yaml.php
@@ -10,7 +10,7 @@ class Yaml extends FileDriver
 {
     protected function dumpContent(Model $model): string
     {
-        $data = array_filter($model->getAttributes());
+        $data = array_filter($this->getModelAttributes($model));
 
         return SymfonyYaml::dump($data);
     }


Test patch:
diff --git a/tests/AdvancedOrbitalTest.php b/tests/AdvancedOrbitalTest.php
index 1d9aa80..febb46e 100644
--- a/tests/AdvancedOrbitalTest.php
+++ b/tests/AdvancedOrbitalTest.php
@@ -2,6 +2,7 @@
 
 namespace Orbit\Tests;
 
+use Orbit\Tests\Fixtures\Post;
 use Orbit\Tests\Fixtures\CustomKey;
 use Orbit\Tests\Fixtures\JsonModel;
 use Orbit\Tests\Fixtures\MarkdownJsonModel;
@@ -14,6 +15,7 @@ class AdvancedOrbitalTest extends TestCase
         CustomKey::all()->each->delete();
         JsonModel::all()->each->delete();
         YamlModel::all()->each->delete();
+        Post::all()->each->delete();
     }
 
     public function test_it_can_create_files_using_custom_primary_key()
@@ -67,4 +69,18 @@ class AdvancedOrbitalTest extends TestCase
 
         $this->assertFileExists(__DIR__.'/content/markdown_json_models/'.$md->getKey().'.md');
     }
+
+    public function test_it_writes_hidden_columns()
+    {
+        $post = Post::create([
+            'title' => 'Ryan',
+            'slug' => 'ryan',
+        ]);
+
+        $this->assertFileExists($path = __DIR__.'/content/posts/'.$post->getKey().'.md');
+
+        $contents = file_get_contents($path);
+
+        $this->assertStringContainsString('slug', $contents);
+    }
 }
diff --git a/tests/Fixtures/Post.php b/tests/Fixtures/Post.php
index fc3f7a4..c06304c 100644
--- a/tests/Fixtures/Post.php
+++ b/tests/Fixtures/Post.php
@@ -12,10 +12,15 @@ class Post extends Model
 
     protected $guarded = [];
 
+    protected $hidden = [
+        'slug',
+    ];
+
     public static function schema(Blueprint $table)
     {
         $table->id();
         $table->string('title');
+        $table->string('slug')->nullable();
         $table->text('content')->nullable();
     }
 }
diff --git a/tests/SoftDeletesTest.php b/tests/SoftDeletesTest.php
index e0a84a0..7054150 100644
--- a/tests/SoftDeletesTest.php
+++ b/tests/SoftDeletesTest.php
@@ -23,7 +23,7 @@ class SoftDeletesTest extends TestCase
 
         $file = file_get_contents(__DIR__.'/content/soft_deleted_posts/'.$post->id.'.md');
 
-        $this->assertStringContainsString(sprintf('deleted_at: \'%s\'', $post->deleted_at->toIsoString()), $file);
+        $this->assertStringContainsString(sprintf('deleted_at: %s', $post->deleted_at->toIso8601String()), $file);
     }
 
     public function test_it_will_delete_file_when_force_deleting()
@@ -38,7 +38,7 @@ class SoftDeletesTest extends TestCase
 
         $file = file_get_contents(__DIR__.'/content/soft_deleted_posts/'.$post->id.'.md');
 
-        $this->assertStringContainsString(sprintf('deleted_at: \'%s\'', $post->deleted_at->toIsoString()), $file);
+        $this->assertStringContainsString(sprintf('deleted_at: %s', $post->deleted_at->toIso8601String()), $file);
 
         $post->forceDelete();
 
@@ -57,7 +57,7 @@ class SoftDeletesTest extends TestCase
 
         $file = file_get_contents(__DIR__.'/content/soft_deleted_posts/'.$post->id.'.md');
 
-        $this->assertStringContainsString(sprintf('deleted_at: \'%s\'', $post->deleted_at->toIsoString()), $file);
+        $this->assertStringContainsString(sprintf('deleted_at: %s', $post->deleted_at->toIso8601String()), $file);
 
         $post->restore();