Files
RomhackPlaza/app/Services/SubmissionsService.php
2026-05-20 18:25:15 +02:00

389 lines
12 KiB
PHP

<?php
namespace App\Services;
use App\Exceptions\SubmissionException;
use App\Helpers\EntryHelpers;
use App\Http\Requests\StoreEntryRequest;
use App\Models\Author;
use App\Models\Entry;
use App\Models\EntryFile;
use App\Models\EntryGallery;
use App\Models\EntryHash;
use App\Models\Game;
use App\Models\Genre;
use App\Models\Language;
use App\Models\Modification;
use App\Models\Platform;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
/**
* @phpstan-import-type FSFileData from \App\Types\FSTypes
*/
class SubmissionsService {
/**
* Request for store/edit.
* @var StoreEntryRequest|null
*/
private ?StoreEntryRequest $request = null;
/**
* Section for store/edit.
* @var string|null
*/
private ?string $section = null;
/**
* @return list<FSFileData>
*/
public function prepareOldFiles( ?Entry $entry = null ): array
{
if( $entry === null ){
$files = old( 'files_uuid', [] );
} else {
$files = old( 'files_uuid', $entry->files->pluck('file_uuid')->toArray() );
}
if( $files === [] )
return [];
return array_map(
function( string $uuid ) {
$file = EntryFile::where('file_uuid', $uuid)->first();
if( $file )
return [
'name' => $file->filename,
'totalChunks' => 0, // Already uploaded.
'rawFile' => null,
'progressValue' => 0,
'currentChunk' => 0,
'done' => true,
'error' => null,
'uuid' => $uuid
];
$file = Cache::get("uploaded_file_{$uuid}");
if( $file )
return [
'name' => $file['filename'],
'totalChunks' => 0, // Already uploaded.
'rawFile' => null,
'progressValue' => 0,
'currentChunk' => 0,
'done' => true,
'error' => null,
'uuid' => $uuid
];
return null;
},
$files );
}
/**
* @param StoreEntryRequest $request
* @param string $section
*
* @return Entry
* @throws SubmissionException
* @throws \Throwable
*/
public function storeEntry( StoreEntryRequest $request, string $section ){
// STEP 1 : Prepare basic fields.
$this->request = $request;
$this->section = $section;
$user_id = 0; // TODO: Replace that.
$entry = DB::transaction(function () use ( $user_id ) {
// STEP 2 : Create game.
$gameId = null;
if( section_must_be( ['romhacks', 'translations'], $this->section ) ){
$gameId = $this->Step2_CreateAndReturnGameId();
}
// STEP 3 : Create Complete title.
$completeTitle = $this->Step3_BuildCompleteTitle( $gameId );
// STEP 4 : Generate slug and entry title.
$entrySlug = EntryHelpers::uniqueSlug( $completeTitle, Entry::class );
if( section_must_be( 'translations', $this->section ) &&
!$this->request->input('entry_title') ){
$entryTitle = Game::find($gameId)->name;
} else {
$entryTitle = $this->request->input('entry_title');
}
// STEP 5 : Removed / Delayed.
// $mainImage = $this->Step5_MoveMainImage();
// STEP 6 : Prepare entry fields and save entry.
$fields = [
'type' => $this->section,
'title' => $entryTitle,
'slug' => $entrySlug,
'description' => $this->request->input('description'),
'main_image' => $this->request->input('main-image'),
'state' => $this->request->input('submit-state'),
'game_id' => $gameId,
'status_id' => $this->request->input('status'),
'version' => $this->request->input('version'),
'release_date' => $this->request->input('release-date'),
'staff_credits' => $this->request->input('staff_credits'),
'relevant_link' => $this->request->input('release_site'),
'youtube_link' => $this->request->input('youtube_video'),
'user_id' => $user_id,
'complete_title' => $completeTitle,
];
$entry = Entry::create( $fields );
// STEP 7 : Save entry fields.
$this->Step7_SaveEntryFiles( $entry->id );
// STEP 8 : Save hashes.
$this->Step8_SaveHashes( $entry->id );
// STEP 9 : Save Authors.
$this->Step9_SaveAuthors( $entry );
// STEP 10 : Save Modifications.
if( section_must_be( 'romhacks', $this->section ) ){
$this->Step10_SaveRomhacksModifications( $entry );
}
// STEP 11 : Save Languages
$this->Step11_SaveLanguages( $entry );
// STEP 12 : Prepare Gallery images.
$this->Step12a_PrepareGalleryImages( $entry );
return $entry;
});
// Step 12, Move main image and gallery.
$this->Step12b_MoveMainImage( $entry );
$this->Step12c_SaveGalleryImages( $entry );
return $entry;
}
/**
* @return int
*
* @throws SubmissionException
*/
private function Step2_CreateAndReturnGameId(): int {
// Already existing game.
if( $this->request->input('game_id') )
return $this->request->input('game_id');
// Need to create a game.
if( !$this->request->input('new-game-title') || !$this->request->input('new-game-platform') || !$this->request->input('new-game-genre') )
throw new SubmissionException( "New game informations is missing" );
$platform = Platform::find( $this->request->input('new-game-platform') );
$genre = Genre::find( $this->request->input('new-game-genre') );
if( !$platform || !$genre )
throw new SubmissionException( "Incorrect game platform id" );
$gameSlug = EntryHelpers::uniqueSlug( $this->request->input('new-game-title'), Game::class );
$game = Game::create([
'name' => trim( $this->request->input('new-game-title') ),
'slug' => $gameSlug,
'platform_id' => $platform->id,
'genre_id' => $genre->id,
]);
return $game->id;
}
/**
* Prepare and build complete title.
*
* @param int|null $gameId
*
* @return string
*/
private function Step3_BuildCompleteTitle( ?int $gameId = null ): string {
$fields = [];
$fields['entry_title'] = $this->request->input('entry_title') ?? null;
if( section_must_be( [ 'homebrew', 'translations' ], $this->section ) && $gameId ){
$fields['game_name'] = Game::find( $gameId )->name;
}
if( section_must_be( 'translations', $this->section ) ) {
$fields['languages_string'] = Language::whereIn('id', $this->request->input('languages', []))->pluck('name')->implode(', ');
}
if( section_must_be(['romhacks', 'homebrew', 'lua-scripts', 'tutorials'], $this->section ) ) {
// TODO: Add single platform ID compatibility.
$fields['platform_name'] = Game::find( $gameId )->platform->name;
}
return EntryHelpers::buildCompleteTitle( $this->section, $fields );
}
/**
* @param int $entryId
*
* @return void
* @throws SubmissionException
*/
private function Step7_SaveEntryFiles( int $entryId ): void
{
foreach ( $this->request->input('files_uuid', [] ) as $uuid ) {
$fileData = Cache::pull("uploaded_file_{$uuid}");
if( !$fileData )
throw new SubmissionException( "File {$uuid} has expired. Please delete all your files and retry." );
EntryFile::create([
'entry_id' => $entryId,
'file_uuid' => $uuid,
'filename' => $fileData['filename'],
'filepath' => $fileData['filepath'],
'favorite_server' => $fileData['favorite_server'],
'favorite_at' => \DateTimeImmutable::createFromTimestamp( $fileData['favorite_at'] ),
'filesize' => $fileData['filesize'],
'state' => 'public'
]);
}
}
/**
* @param int $entryId
*
* @return void
*/
private function Step8_SaveHashes( int $entryId ): void
{
foreach ( $this->request->input('hashes', [] ) as $hash ) {
if( !isset($hash['filename'], $hash['hash_crc32'], $hash['hash_sha1'], $hash['verified']) ) {
continue;
}
EntryHash::create([
'entry_id' => $entryId,
'filename' => $hash['filename'],
'hash_crc32' => $hash['hash_crc32'],
'hash_sha1' => $hash['hash_sha1'],
'verified' => $hash['verified'],
]);
}
}
/**
* @param Entry $entry
*
* @return void
* @throws SubmissionException
*/
private function Step9_SaveAuthors( Entry $entry ): void
{
// Existing authors.
foreach ( $this->request->input('authors', [] ) as $authorId ) {
$author = Author::find( $authorId );
if( !$author )
throw new SubmissionException( "Author {$authorId} does not exist." );
$entry->authors()->attach( $author->id );
}
// New Authors
foreach ( $this->request->input('new-authors', [] ) as $authorName ) {
$authorName = trim( $authorName );
if( $authorName === '' )
continue;
$author = Author::firstOrCreate(
['slug' => EntryHelpers::uniqueSlug( $authorName, Author::class )],
['name' => $authorName]
);
$entry->authors()->attach( $author->id );
}
}
/**
* @param Entry $entry
*
* @return void
* @throws SubmissionException
*/
private function Step10_SaveRomhacksModifications( Entry $entry ): void
{
foreach ( $this->request->input('modifications', [] ) as $modificationId ) {
$modification = Modification::find( $modificationId );
if( !$modification )
throw new SubmissionException( "Modification {$modificationId} does not exist." );
$entry->modifications()->attach( $modification->id );
}
}
/**
* @param Entry $entry
*
* @return void
* @throws SubmissionException
*/
private function Step11_SaveLanguages( Entry $entry ): void
{
foreach ( $this->request->input('languages', [] ) as $languageId ) {
$language = Language::find( $languageId );
if( !$language )
throw new SubmissionException( "Language {$languageId} does not exist." );
$entry->languages()->attach( $language->id );
}
}
private function Step12a_PrepareGalleryImages( Entry $entry ): void
{
foreach ( $this->request->input('gallery', [] ) as $imagePath ) {
EntryGallery::create([
'entry_id' => $entry->id,
'image' => $imagePath,
]);
}
}
/**
* @param Entry $entry
*
* @return void
*/
private function Step12b_MoveMainImage( Entry $entry ): void {
$mainImage = $entry->main_image;
$newPath = 'entries/main-images/' . basename($mainImage);
if( !Storage::disk('public')->move($mainImage, $newPath) )
return;
$entry->update(['main_image' => $newPath]);
}
private function Step12c_SaveGalleryImages( Entry $entry ): void
{
foreach ( $entry->gallery as $galleryItem ) {
$newPath = 'entries/gallery-images/' . $entry->id . '/' . basename($galleryItem->image);
if( !Storage::disk('public')->move($galleryItem->image, $newPath) )
continue;
$galleryItem->update(['image' => $newPath]);
}
}
}