Reputation: 383
I have a method for save some files in my models and I don't wanna duplicate it anymore, which is the best way to avoid this code duplication in Laravel?
You can see bellow some examples of the duplication, there it is the Product and the Articles Models, both have the saveFile method.
How can I isolate this code and reuse it?
// App/Article.php
class Product extends Model {
protected static $storageFolders = "public/products";
public static function saveFile($file, Array $options = []) {
$filename = "";
if (isset($options["name"])) {
$filename .= $options["name"];
}
if ($options["unique"]) {
$filename .= "-" . time();
}
$picture_path = "";
if ($filename) {
$extension = $file->getClientOriginalExtension();
$filename .= ".$extension";
$picture_path = $file->storeAs(SELF::$storageFolders, $filename);
} else {
$picture_path = $file->store(SELF::$storageFolders);
}
$storage_url = Storage::url($picture_path);
return $storage_url;
}
}
// App/Article.php
class Article extends Model {
protected static $storageFolders = "public/articles";
public static function saveFile($file, Array $options = []) {
$filename = "";
if (isset($options["name"])) {
$filename .= $options["name"];
}
if ($options["unique"]) {
$filename .= "-" . time();
}
$picture_path = "";
if ($filename) {
$extension = $file->getClientOriginalExtension();
$filename .= ".$extension";
$picture_path = $file->storeAs(SELF::$storageFolders, $filename);
} else {
$picture_path = $file->store(SELF::$storageFolders);
}
$storage_url = Storage::url($picture_path);
return $storage_url;
}
}
Upvotes: 1
Views: 647
Reputation: 1213
You can use a Service or a Trait. (you never call method from one controller to another controller).
<?php
namespace App\Services;
class FileService
{
public function saveFile($file, Array $options = []) {
$filename = "";
if (isset($options["name"])) {
$filename .= $options["name"];
}
if ($options["unique"]) {
$filename .= "-" . time();
}
$picture_path = "";
if ($filename) {
$extension = $file->getClientOriginalExtension();
$filename .= ".$extension";
$picture_path = $file->storeAs(SELF::$storageFolders, $filename);
} else {
$picture_path = $file->store(SELF::$storageFolders);
}
$storage_url = Storage::url($picture_path);
return $storage_url;
}
}
Then, in every other class, you just initialize it in constructor, and use.
// App/Article.php
class Product extends Model {
protected static $storageFolders = "public/products";
protected $fileService;
public function __construct(FileService $fileService)
{
$this->fileService = $fileService;
}
public function saveFile ($file, Array $options = [] ) {
this->fileService->saveFile($file,$options);
}
Upvotes: 1
Reputation: 9476
Traits allow you to share methods between different classes. If you put that method in a trait and have both classes use it, that should achieve what you want.
For example:
trait SavesFiles
{
public static function saveFile($file, Array $options = []) {
$filename = "";
if (isset($options["name"])) {
$filename .= $options["name"];
}
if ($options["unique"]) {
$filename .= "-" . time();
}
$picture_path = "";
if ($filename) {
$extension = $file->getClientOriginalExtension();
$filename .= ".$extension";
$picture_path = $file->storeAs(SELF::$storageFolders, $filename);
} else {
$picture_path = $file->store(SELF::$storageFolders);
}
$storage_url = Storage::url($picture_path);
return $storage_url;
}
}
Then, your models could use it as follows:
class Product extends Model
{
use SavesFiles;
...
}
Anything that differs between models, such as the folder, can be defined on the class, eg public $folder = 'products';
, and then used in the trait, eg $this->folder
.
Or, you could create an abstract model class with that method and have both models inherit from it. But traits would be my first choice.
Upvotes: 4