Skip to content

Commit

Permalink
feat: Sync media with tests
Browse files Browse the repository at this point in the history
  • Loading branch information
FarhanShares committed Oct 6, 2023
1 parent 3bc9c5f commit 72cbafe
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 46 deletions.
124 changes: 88 additions & 36 deletions src/Traits/HasMedia.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use Illuminate\Database\Eloquent\Collection;
use FarhanShares\MediaMan\Jobs\PerformConversions;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Support\Collection as BaseCollection;

trait HasMedia
{
Expand Down Expand Up @@ -89,45 +91,16 @@ public function getFirstMediaUrl(?string $channel = 'default', string $conversio
*/
public function attachMedia($media, string $channel = 'default', array $conversions = [])
{
$this->registerMediaChannels();

$ids = $this->parseMediaIds($media);

$mediaChannel = $this->getMediaChannel($channel);

if ($mediaChannel && $mediaChannel->hasConversions()) {
$conversions = array_merge(
$conversions,
$mediaChannel->getConversions()
);
}
// Utilize syncMedia with detaching set to false to achieve the attach behavior
$syncResult = $this->syncMedia($media, $channel, $conversions, false);

if (!empty($conversions)) {
$model = config('mediaman.models.media');
if (!isset($syncResult['attached'])) return null;

$media = $model::findMany($ids);
// Count the number of attached media from the sync result
$attached = count($syncResult['attached'] ?? []);

$media->each(function ($media) use ($conversions) {
PerformConversions::dispatch(
$media,
$conversions
);
});
}


$mappedIds = [];
foreach ($ids as $id) {
$mappedIds[$id] = ['channel' => $channel];
}

try {
$res = $this->media()->sync($mappedIds, false);
$attached = count($res['attached']);
return $attached > 0 ? $attached : null;
} catch (Throwable $th) {
return null;
}
// Return the count of attached media if there's any, otherwise return null
return $attached > 0 ? $attached : null;
}

/**
Expand Down Expand Up @@ -208,4 +181,83 @@ public function clearMediaChannel(string $channel = 'default')
{
$this->media()->wherePivot('channel', $channel)->detach();
}


/**
* Sync media to the specified channel.
*
* This will remove the media that aren't in the provided list
* and add those which aren't already attached if $detaching is truthy.
*
* @param mixed $media
* @param string $channel
* @param array $conversions
* @param bool $detaching
* @return array|null
*/
public function syncMedia($media, string $channel = 'default', array $conversions = [], $detaching = true)
{
$this->registerMediaChannels();

if ($detaching === true && $this->shouldDetachAll($media)) {
return $this->media()->sync([]);
}

$ids = $this->parseMediaIds($media);

$mediaChannel = $this->getMediaChannel($channel);

if ($mediaChannel && $mediaChannel->hasConversions()) {
$conversions = array_merge(
$conversions,
$mediaChannel->getConversions()
);
}

if (!empty($conversions)) {
$model = config('mediaman.models.media');

$mediaInstances = $model::findMany($ids);

$mediaInstances->each(function ($mediaInstance) use ($conversions) {
PerformConversions::dispatch(
$mediaInstance,
$conversions
);
});
}

$mappedIds = [];
foreach ($ids as $id) {
$mappedIds[$id] = ['channel' => $channel];
}

try {
$res = $this->media()->sync($mappedIds, $detaching);
return $res; // this should give an array containing 'attached', 'detached', and 'updated'
} catch (Throwable $th) {
return null;
}
}

/**
* Check if all media should be detached
*
* bool|null|empty-string|empty-array to detach all media
*
* @param mixed $collections
* @return boolean
*/
protected function shouldDetachAll($media): bool
{
if (is_bool($media) || is_null($media) || empty($media)) {
return true;
}

if (is_countable($media) && count($media) === 0) {
return true;
}

return false;
}
}
94 changes: 84 additions & 10 deletions tests/HasMediaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
namespace FarhanShares\MediaMan\Tests;


use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use FarhanShares\MediaMan\Models\Media;
use FarhanShares\MediaMan\MediaUploader;
use FarhanShares\MediaMan\Tests\Models\Subject;
use FarhanShares\MediaMan\Jobs\PerformConversions;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;

class HasMediaTest extends TestCase
{
Expand Down Expand Up @@ -80,15 +82,19 @@ public function it_returns_number_of_attached_media_or_null_while_associating()
{
$media = factory(Media::class)->create();

$attached = $this->subject->attachMedia($media, 'custom');
$attachedCount = $this->subject->attachMedia($media, 'custom');

$this->assertEquals(1, $attached);
$this->assertEquals(1, $attachedCount);

// todo: couldn't test null return type with sqlite test environment
// todo: as sqlite doesn't have relationship, it won't fail, but it works on relational db
// try attaching a non-existing media record
// $attached = $this->subject->attachMedia(5, 'custom');
// $this->assertEquals(null, $attached);
if (DB::connection() instanceof \Illuminate\Database\SQLiteConnection) {
// SQLite doesn't enforce foreign key constraints by default, so this test won't fail as expected in an SQLite environment.
// However, it should work as expected on other relational databases that enforce these constraints.
$this->markTestSkipped('Skipping test for SQLite connection.');
} else {
// try attaching a non-existing media record
$attached = $this->subject->attachMedia(5, 'custom');
$this->assertEquals(null, $attached);
}
}

/** @test */
Expand All @@ -106,6 +112,74 @@ public function it_returns_number_of_detached_media_or_null_while_disassociating
$this->assertEquals(null, $detached);
}

public function it_can_attach_media_and_returns_number_of_media_attached()
{
$media = factory(Media::class)->create();

$attachedCount = $this->subject->attachMedia($media);

$this->assertEquals(1, $attachedCount);

$attachedMedia = $this->subject->media()->first();
$this->assertEquals($media->id, $attachedMedia->id);
}

/** @test */
public function it_can_detach_media_and_returns_number_of_media_detached()
{
$media = factory(Media::class)->create();
$this->subject->attachMedia($media);

$detachedCount = $this->subject->detachMedia($media);

$this->assertEquals(1, $detachedCount);
$this->assertNull($this->subject->media()->first());
}

/** @test */
public function it_can_sync_media_and_returns_sync_status()
{
$media1 = MediaUploader::source($this->fileOne)
->useName('image')
->useCollection('default')
->useDisk('default')
->upload();
$media2 = MediaUploader::source($this->fileOne)
->useName('image')
->useCollection('default')
->useDisk('default')
->upload();

// Initially attach media1
$this->subject->attachMedia($media1);

// Now, sync to media2
$syncStatus = $this->subject->syncMedia($media2);

$this->assertArrayHasKey('updated', $syncStatus);
$this->assertArrayHasKey('attached', $syncStatus);
$this->assertArrayHasKey('detached', $syncStatus);

$this->assertEquals([$media2->id], $syncStatus['attached']);
$this->assertEquals([$media1->id], $syncStatus['detached']);

$syncStatus = $this->subject->syncMedia([]); // should detach all
$this->assertEquals(1, count($syncStatus['detached']));
}

/** @test */
public function it_can_sync_collections_for_a_media_instance()
{
$media = factory(Media::class)->create();
$collections = ['collection1', 'collection2'];

$syncStatus = $media->syncCollections($collections);

$this->assertArrayHasKey('attached', $syncStatus);
$this->assertArrayHasKey('detached', $syncStatus);
$this->assertArrayHasKey('updated', $syncStatus);
}

/** @test */
public function it_will_perform_the_given_conversions_when_media_is_attached()
{
Expand Down

0 comments on commit 72cbafe

Please sign in to comment.