firstcommit
This commit is contained in:
0
Modules/Blog/app/Http/Controllers/.gitkeep
Normal file
0
Modules/Blog/app/Http/Controllers/.gitkeep
Normal file
142
Modules/Blog/app/Http/Controllers/BlogController.php
Normal file
142
Modules/Blog/app/Http/Controllers/BlogController.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\app\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Jobs\SendNewBlogNotification;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Modules\Blog\app\Repositories\BlogRepository;
|
||||
use Modules\Blog\app\Http\Requests\CreateBlogRequest;
|
||||
|
||||
class BlogController extends Controller
|
||||
{
|
||||
protected $blogRepository;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->blogRepository = new BlogRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$perPage = $request->has('per-page') ? $request->input('per-page') : null;
|
||||
$filter = $request->has('filter') ? $request->input('filter') : [];
|
||||
$blogs = $this->blogRepository->allBlogs($perPage, $filter);
|
||||
|
||||
return view('blog::index', compact('blogs'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
return view('blog::create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(CreateBlogRequest $request)
|
||||
{
|
||||
try {
|
||||
$validated = $request->validated();
|
||||
|
||||
$blog = $this->blogRepository->storeBlog($validated);
|
||||
|
||||
dispatch(new SendNewBlogNotification($blog));
|
||||
|
||||
toastr()->success('Blog created successfully.');
|
||||
|
||||
return redirect()->route('cms.blogs.index');
|
||||
} catch (\Throwable $th) {
|
||||
report($th);
|
||||
toastr()->error('Something went wrong.');
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the specified resource.
|
||||
*/
|
||||
public function show($id)
|
||||
{
|
||||
return view('blog::show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit($uuid)
|
||||
{
|
||||
$blog = $this->blogRepository->findBlogByUuid($uuid);
|
||||
|
||||
if (!$blog) {
|
||||
toastr()->error('Blog not found.');
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
return view('blog::edit', compact('blog'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(CreateBlogRequest $request, $uuid): RedirectResponse
|
||||
{
|
||||
try {
|
||||
$validated = $request->validated();
|
||||
$blog = $this->blogRepository->updateBlog($validated, $uuid);
|
||||
|
||||
if (!$blog) {
|
||||
toastr()->error('Blog not found !');
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
toastr()->success('Blog updated successfully.');
|
||||
|
||||
return redirect()->route('cms.blogs.index');
|
||||
} catch (\Throwable $th) {
|
||||
report($th);
|
||||
toastr()->error('Something went wrong.');
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy($uuid)
|
||||
{
|
||||
try {
|
||||
$blog = $this->blogRepository->deleteBlog($uuid);
|
||||
|
||||
if (!$blog) {
|
||||
toastr()->error('Blog not found.');
|
||||
|
||||
return back();
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
|
||||
toastr()->success('Blog deleted successfully.');
|
||||
|
||||
return redirect()->route('cms.blogs.index');
|
||||
} catch (\Throwable $th) {
|
||||
DB::rollback();
|
||||
report($th);
|
||||
toastr()->error('Something went wrong.');
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
}
|
0
Modules/Blog/app/Http/Middleware/.gitkeep
Normal file
0
Modules/Blog/app/Http/Middleware/.gitkeep
Normal file
0
Modules/Blog/app/Http/Requests/.gitkeep
Normal file
0
Modules/Blog/app/Http/Requests/.gitkeep
Normal file
76
Modules/Blog/app/Http/Requests/CreateBlogRequest.php
Normal file
76
Modules/Blog/app/Http/Requests/CreateBlogRequest.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\app\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CreateBlogRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'required|string|max:255',
|
||||
'content' => 'required|string',
|
||||
'author' => 'required|string|max:255',
|
||||
'summary' => 'required|string',
|
||||
'published_date' => 'required|date',
|
||||
'status' => 'required|in:active,inactive',
|
||||
'meta_title' => 'sometimes|nullable|string|max:255',
|
||||
'meta_description' => 'sometimes|nullable|string',
|
||||
'meta_keywords' => 'sometimes|nullable|string|max:255',
|
||||
'image' => 'sometimes|nullable|mimes:png,jpg,jpeg',
|
||||
'slug' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'title.required' => 'The title field is required.',
|
||||
'title.string' => 'The title field must be a string.',
|
||||
'title.max' => 'The title may not be greater than 255 characters.',
|
||||
|
||||
'content.required' => 'The content field is required.',
|
||||
'content.string' => 'The content field must be a string.',
|
||||
|
||||
'author.required' => 'The author field is required.',
|
||||
'author.string' => 'The author field must be a string.',
|
||||
'author.max' => 'The author may not be greater than 255 characters.',
|
||||
|
||||
'summary.required' => 'The summary field is required.',
|
||||
'summary.string' => 'The summary field must be a string.',
|
||||
|
||||
'published_date.required' => 'The published date field is required.',
|
||||
'published_date.date' => 'The published date field must be a valid date.',
|
||||
|
||||
'status.required' => 'The status field is required.',
|
||||
'status.in' => 'The status field must be either "active" or "inactive".',
|
||||
|
||||
'meta_title.string' => 'The meta title field must be a string.',
|
||||
'meta_title.max' => 'The meta title may not be greater than 255 characters.',
|
||||
|
||||
'meta_description.string' => 'The meta description field must be a string.',
|
||||
|
||||
'meta_keywords.string' => 'The meta keywords field must be a string.',
|
||||
'meta_keywords.max' => 'The meta keywords may not be greater than 255 characters.',
|
||||
|
||||
'image.mimes' => 'The image must be a file of type: png, jpg, jpeg.',
|
||||
|
||||
'slug.required' => 'The slug field is required.',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
// return auth()->user()->can('users.create');
|
||||
}
|
||||
}
|
0
Modules/Blog/app/Models/.gitkeep
Normal file
0
Modules/Blog/app/Models/.gitkeep
Normal file
62
Modules/Blog/app/Models/Blog.php
Normal file
62
Modules/Blog/app/Models/Blog.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\app\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Modules\Blog\Database\factories\BlogFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Blog extends Model
|
||||
{
|
||||
// use HasFactory;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $dates = ['published_date'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'uuid',
|
||||
'title',
|
||||
'summary',
|
||||
'content',
|
||||
'author',
|
||||
'published_date',
|
||||
'image',
|
||||
'image_path',
|
||||
'status',
|
||||
'slug',
|
||||
];
|
||||
|
||||
|
||||
protected $casts = [
|
||||
'published_date' => 'date:Y-m-d H:i:s',
|
||||
];
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function getFullImageAttribute()
|
||||
{
|
||||
$result = null;
|
||||
|
||||
if ($this->image_path) {
|
||||
$result = asset('storage/uploads/' . $this->image_path);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function blogMeta()
|
||||
{
|
||||
return $this->hasOne(BlogMeta::class, 'blog_id');
|
||||
}
|
||||
|
||||
|
||||
// protected static function newFactory(): BlogFactory
|
||||
// {
|
||||
// //return BlogFactory::new();
|
||||
// }
|
||||
}
|
33
Modules/Blog/app/Models/BlogMeta.php
Normal file
33
Modules/Blog/app/Models/BlogMeta.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\app\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Modules\Blog\Database\factories\BlogMetaFactory;
|
||||
|
||||
class BlogMeta extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'blog_id',
|
||||
'meta_title',
|
||||
'meta_description',
|
||||
'meta_keywords',
|
||||
];
|
||||
|
||||
public function blog()
|
||||
{
|
||||
return $this->belongsTo(Blog::class, 'blog_id');
|
||||
}
|
||||
|
||||
// protected static function newFactory(): BlogMetaFactory
|
||||
// {
|
||||
// //return BlogMetaFactory::new();
|
||||
// }
|
||||
}
|
0
Modules/Blog/app/Providers/.gitkeep
Normal file
0
Modules/Blog/app/Providers/.gitkeep
Normal file
114
Modules/Blog/app/Providers/BlogServiceProvider.php
Normal file
114
Modules/Blog/app/Providers/BlogServiceProvider.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\app\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class BlogServiceProvider extends ServiceProvider
|
||||
{
|
||||
protected string $moduleName = 'Blog';
|
||||
|
||||
protected string $moduleNameLower = 'blog';
|
||||
|
||||
/**
|
||||
* Boot the application events.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->registerCommands();
|
||||
$this->registerCommandSchedules();
|
||||
$this->registerTranslations();
|
||||
$this->registerConfig();
|
||||
$this->registerViews();
|
||||
$this->loadMigrationsFrom(module_path($this->moduleName, 'database/migrations'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->app->register(RouteServiceProvider::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register commands in the format of Command::class
|
||||
*/
|
||||
protected function registerCommands(): void
|
||||
{
|
||||
// $this->commands([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register command Schedules.
|
||||
*/
|
||||
protected function registerCommandSchedules(): void
|
||||
{
|
||||
// $this->app->booted(function () {
|
||||
// $schedule = $this->app->make(Schedule::class);
|
||||
// $schedule->command('inspire')->hourly();
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
* Register translations.
|
||||
*/
|
||||
public function registerTranslations(): void
|
||||
{
|
||||
$langPath = resource_path('lang/modules/'.$this->moduleNameLower);
|
||||
|
||||
if (is_dir($langPath)) {
|
||||
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
|
||||
$this->loadJsonTranslationsFrom($langPath);
|
||||
} else {
|
||||
$this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
|
||||
$this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register config.
|
||||
*/
|
||||
protected function registerConfig(): void
|
||||
{
|
||||
$this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config');
|
||||
$this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register views.
|
||||
*/
|
||||
public function registerViews(): void
|
||||
{
|
||||
$viewPath = resource_path('views/modules/'.$this->moduleNameLower);
|
||||
$sourcePath = module_path($this->moduleName, 'resources/views');
|
||||
|
||||
$this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']);
|
||||
|
||||
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
|
||||
|
||||
$componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.config('modules.paths.generator.component-class.path'));
|
||||
Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*/
|
||||
public function provides(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function getPublishableViewPaths(): array
|
||||
{
|
||||
$paths = [];
|
||||
foreach (config('view.paths') as $path) {
|
||||
if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
|
||||
$paths[] = $path.'/modules/'.$this->moduleNameLower;
|
||||
}
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
}
|
59
Modules/Blog/app/Providers/RouteServiceProvider.php
Normal file
59
Modules/Blog/app/Providers/RouteServiceProvider.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\app\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The module namespace to assume when generating URLs to actions.
|
||||
*/
|
||||
protected string $moduleNamespace = 'Modules\Blog\app\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Called before routes are registered.
|
||||
*
|
||||
* Register any model bindings or pattern based filters.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*/
|
||||
public function map(): void
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
$this->mapWebRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "web" routes for the application.
|
||||
*
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
*/
|
||||
protected function mapWebRoutes(): void
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->moduleNamespace)
|
||||
->group(module_path('Blog', '/routes/web.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the "api" routes for the application.
|
||||
*
|
||||
* These routes are typically stateless.
|
||||
*/
|
||||
protected function mapApiRoutes(): void
|
||||
{
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->moduleNamespace)
|
||||
->group(module_path('Blog', '/routes/api.php'));
|
||||
}
|
||||
}
|
152
Modules/Blog/app/Repositories/BlogRepository.php
Normal file
152
Modules/Blog/app/Repositories/BlogRepository.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\app\Repositories;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Modules\Banner\app\Services\FileManagementService;
|
||||
use Modules\Blog\app\Models\Blog;
|
||||
use Modules\Blog\app\Models\BlogMeta;
|
||||
|
||||
class BlogRepository
|
||||
{
|
||||
//-- Retrieve all Services
|
||||
public function allBlogs($perPage = null, $filter = [], $sort = ['by' => 'id', 'sort' => 'DESC'])
|
||||
{
|
||||
return Blog::with('blogMeta')->when(array_keys($filter, true), function ($query) use ($filter) {
|
||||
if (!empty($filter['title'])) {
|
||||
$query->where('title', $filter['title']);
|
||||
}
|
||||
if (!empty($filter['author'])) {
|
||||
$query->where('author', 'like', '%' . $filter['author'] . '%');
|
||||
}
|
||||
})
|
||||
->orderBy($sort['by'], $sort['sort'])
|
||||
->paginate($perPage ?: env('PAGE_LIMIT', 999));
|
||||
}
|
||||
|
||||
//-- Find Blog by uuid
|
||||
public function findBlogByUuid($uuid)
|
||||
{
|
||||
return Blog::where('uuid', $uuid)->first();
|
||||
}
|
||||
|
||||
public function storeBlog(array $validated)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$blog = new Blog();
|
||||
$blog->uuid = Str::uuid();
|
||||
$blog->title = $validated['title'];
|
||||
$blog->content = $validated['content'];
|
||||
$blog->author = $validated['author'];
|
||||
$blog->summary = $validated['summary'];
|
||||
$blog->published_date = $validated['published_date'];
|
||||
$blog->status = $validated['status'];
|
||||
$blog->slug = $validated['slug'];
|
||||
$blog->save();
|
||||
|
||||
if (isset($validated['image']) && $validated['image']->isValid()) {
|
||||
FileManagementService::storeFile(
|
||||
file: $validated['image'],
|
||||
uploadedFolderName: 'blogs',
|
||||
model: $blog
|
||||
);
|
||||
}
|
||||
|
||||
$blogMeta = new BlogMeta();
|
||||
$blogMeta->uuid = Str::uuid();
|
||||
$blogMeta->meta_title = $validated['meta_title'];
|
||||
$blogMeta->meta_description = $validated['meta_description'];
|
||||
$blogMeta->meta_keywords = $validated['meta_keywords'];
|
||||
$blog->blogMeta()->save($blogMeta);
|
||||
DB::commit();
|
||||
|
||||
return $blog;
|
||||
} catch (\Throwable $th) {
|
||||
report($th);
|
||||
DB::rollback();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function updateBlog($validated, $uuid)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$blog = $this->findBlogByUuid($uuid);
|
||||
if (!$blog) {
|
||||
return null;
|
||||
}
|
||||
$blog->title = $validated['title'];
|
||||
$blog->content = $validated['content'];
|
||||
$blog->author = $validated['author'];
|
||||
$blog->summary = $validated['summary'];
|
||||
$blog->published_date = $validated['published_date'];
|
||||
$blog->status = $validated['status'];
|
||||
$blog->slug = $validated['slug'];
|
||||
$blog->save();
|
||||
|
||||
if (isset($validated['image']) && $validated['image']->isValid()) {
|
||||
FileManagementService::uploadFile(
|
||||
file: $validated['image'],
|
||||
uploadedFolderName: 'blogs',
|
||||
filePath: $blog->image_path,
|
||||
model: $blog
|
||||
);
|
||||
}
|
||||
|
||||
// Update or create blog meta
|
||||
$blogMeta = $blog->blogMeta()->firstOrNew([]);
|
||||
if (!$blogMeta->exists) {
|
||||
$blogMeta->uuid = Str::uuid();
|
||||
}
|
||||
$blogMeta->meta_title = $validated['meta_title'];
|
||||
$blogMeta->meta_description = $validated['meta_description'];
|
||||
$blogMeta->meta_keywords = $validated['meta_keywords'];
|
||||
$blogMeta->save();
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $blog;
|
||||
} catch (\Throwable $th) {
|
||||
dd($th);
|
||||
report($th);
|
||||
DB::rollBack();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
//-- Delete Blog
|
||||
public function deleteBlog(string $uuid)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$blog = $this->findBlogByUuid($uuid);
|
||||
if (!$blog) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Delete the image file associated with the blog
|
||||
if ($blog->image_path !== null) {
|
||||
FileManagementService::deleteFile($blog->image_path);
|
||||
}
|
||||
|
||||
// Delete associated blog meta and the blog itself
|
||||
$blog->blogMeta()->delete();
|
||||
|
||||
$blog->delete();
|
||||
|
||||
DB::commit();
|
||||
|
||||
return $blog;
|
||||
} catch (\Throwable $th) {
|
||||
DB::rollBack();
|
||||
report($th);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
66
Modules/Blog/app/Services/FileManagementService.php
Normal file
66
Modules/Blog/app/Services/FileManagementService.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\app\Services;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class FileManagementService
|
||||
{
|
||||
//-- store file
|
||||
public static function storeFile($file, $uploadedFolderName, $model)
|
||||
{
|
||||
try {
|
||||
$originalFileName = $file->getClientOriginalName();
|
||||
$modifiedFileName = date('YmdHis') . "_" . uniqid() . "." . $originalFileName;
|
||||
|
||||
$file->storeAs($uploadedFolderName, $modifiedFileName, 'public_uploads'); // This line uses 'public_uploads' disk
|
||||
|
||||
$model->image = $modifiedFileName;
|
||||
$model->image_path = $uploadedFolderName . '/' . $modifiedFileName;
|
||||
$model->save();
|
||||
} catch (\Throwable $th) {
|
||||
report($th);
|
||||
toastr()->error('Something went wrong.');
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
||||
//-- update file
|
||||
public static function uploadFile($file, $uploadedFolderName ,$filePath, $model)
|
||||
{
|
||||
try {
|
||||
if ($filePath && Storage::disk('public_uploads')->exists($filePath)) {
|
||||
Storage::disk('public_uploads')->delete($filePath);
|
||||
}
|
||||
|
||||
$originalFileName = $file->getClientOriginalName();
|
||||
$modifiedFileName = date('YmdHis') . "_" . uniqid() . "." . $originalFileName;
|
||||
|
||||
$file->storeAs($uploadedFolderName, $modifiedFileName, 'public_uploads'); // This line uses 'public_uploads' disk
|
||||
|
||||
$model->image = $modifiedFileName;
|
||||
$model->image_path = $uploadedFolderName . '/' . $modifiedFileName;
|
||||
|
||||
$model->save();
|
||||
} catch (\Throwable $th) {
|
||||
report($th);
|
||||
toastr()->error('Something went wrong.');
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-- delete file
|
||||
public static function deleteFile($filePath)
|
||||
{
|
||||
try {
|
||||
if ($filePath && Storage::disk('public_uploads')->exists($filePath)) {
|
||||
Storage::disk('public_uploads')->delete($filePath);
|
||||
} else {
|
||||
toastr()->error('File Not wrong.');
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
report($th);
|
||||
toastr()->error('Something went wrong while deleting the file.');
|
||||
}
|
||||
}
|
||||
}
|
31
Modules/Blog/composer.json
Normal file
31
Modules/Blog/composer.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "nwidart/blog",
|
||||
"description": "",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Widart",
|
||||
"email": "n.widart@gmail.com"
|
||||
}
|
||||
],
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [],
|
||||
"aliases": {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Modules\\Blog\\": "",
|
||||
"Modules\\Blog\\App\\": "app/",
|
||||
"Modules\\Blog\\Database\\Factories\\": "database/factories/",
|
||||
"Modules\\Blog\\Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Modules\\Blog\\Tests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
0
Modules/Blog/config/.gitkeep
Normal file
0
Modules/Blog/config/.gitkeep
Normal file
5
Modules/Blog/config/config.php
Normal file
5
Modules/Blog/config/config.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'Blog',
|
||||
];
|
0
Modules/Blog/database/factories/.gitkeep
Normal file
0
Modules/Blog/database/factories/.gitkeep
Normal file
0
Modules/Blog/database/migrations/.gitkeep
Normal file
0
Modules/Blog/database/migrations/.gitkeep
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('blogs', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->uuid();
|
||||
$table->string('title');
|
||||
$table->string('author');
|
||||
$table->text('summary');
|
||||
$table->text('content');
|
||||
$table->timestamp('published_date');
|
||||
$table->string('image')->nullable();
|
||||
$table->string('image_path')->nullable();
|
||||
$table->string('status')->default('active');
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('blogs');
|
||||
}
|
||||
};
|
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('blog_metas', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->uuid();
|
||||
$table->unsignedBigInteger('blog_id');
|
||||
$table->string('meta_title')->nullable();
|
||||
$table->longText('meta_description')->nullable();
|
||||
$table->longText('meta_keywords')->nullable();
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
$table->foreign('blog_id')->references('id')->on('blogs');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('blog_metas');
|
||||
}
|
||||
};
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('blogs', function (Blueprint $table) {
|
||||
$table->string('slug', 300)->after('title');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('blogs', function (Blueprint $table) {
|
||||
$table->dropColumn('slug');
|
||||
});
|
||||
}
|
||||
};
|
2
Modules/Blog/database/migrations/tempCodeRunnerFile.php
Normal file
2
Modules/Blog/database/migrations/tempCodeRunnerFile.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
blogs
|
0
Modules/Blog/database/seeders/.gitkeep
Normal file
0
Modules/Blog/database/seeders/.gitkeep
Normal file
93
Modules/Blog/database/seeders/BlogDatabaseSeeder.php
Normal file
93
Modules/Blog/database/seeders/BlogDatabaseSeeder.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\Blog\database\seeders;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Modules\Blog\app\Models\Blog;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class BlogDatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$blogs = [
|
||||
[
|
||||
'title' => 'How to Reach Benagil Cave in 2023, Algarve, Portugal (Tours + Kayaking + tips)',
|
||||
'content' => "Algarve’s Benagil Cave beach is one of those extra special ones. The only way in is by kayaking or paddle boating, which obviously adds a more elusive touch to this already amazing place.
|
||||
didn’t know already, the Algarve region is in the South of Portugal and is a lovely coastal area. The thing that makes the Algarve coastline unique is the stunning rock formations. They are everywhere and are mindblowing. I spent a week in Lagos and then a week in Carvoeiro – both these places are in the Algarve and are stunning.
|
||||
In Portuguese, the Benagil Cave is called Algar de Benagil. The holes in the rock formations are called algares (or holes) and there are many of them. I did mention in my Seven Hanging Valleys post that there’s a hike that goes on top of the cliff inside which this cave is situated. You can see the holes in these cliffs from the top.
|
||||
This post is about the best way of getting inside the cave from the water. Actually, there are many caves in this short stretch and they are collectively called the Benagil Caves, but in this post, we will mostly talk about the most famous Benagil Cave one with a hidden sandy beach. THIS one (below).",
|
||||
'author' => 'Jhigu CMS Travel & Tour',
|
||||
'summary' => 'Algarve’s Benagil Cave beach is one of those extra special ones. The only way in is by kayaking or paddle boating, which obviously adds a more elusive touch to this already amazing place',
|
||||
'published_date' => Carbon::now(),
|
||||
'image' => 'b1.jpg',
|
||||
'slug' => "Slug for blog",
|
||||
],
|
||||
[
|
||||
'title' => 'The Ultimate Italy Road Trip: 2 Weeks Itinerary (with Amalfi Coast)',
|
||||
'content' => "The first time I visited Italy, it was just North Italy. We landed in Venice and drove to Trentino in our rental car. The second time was in South Italy where we spent one entire month in Puglia. We actually drove from Germany to Puglia but realized it would have been easier to just fly to Bari or Brindisi and drive a rental car from there.
|
||||
For the purpose of travel, it is important to understand what are the regions of Italy. You can pick and choose some of them or get a taste of them all. Here are the regions in Italy that you can visit –
|
||||
Northeast Italy, (the Dolomites, Trentino, Venice and Bologna)
|
||||
Northwest Italy, (Cinque Terre, Milan and the Alps)
|
||||
Central Italy, (Tuscany region and Rome)
|
||||
Southern Italy, (Naples, Puglia, Amalfi and Capri)
|
||||
The islands – Sicily and Sardinia.
|
||||
If you ever see the list of the most visited countries in the world, Italy usually is in top 5 year after year. It is because there is so much to see & experience in every single region of
|
||||
Keep in mind that to properly explore each region of Italy, you would probably need at least two weeks each. However, this itinerary focuses on the entire Italy, so I will help you move from one region to another and tell you the best of each. That’s the difference between a region-specific itinerary and a country-specific itinerary.
|
||||
Matera in Puglia, Italy road trip two weeks itinerary – via Pixabay
|
||||
If you think you will get to visit Italy multiple times, then by all means pick just one region or maximum two for each trip. If you’re going to visit Italy just once or twice in your life then I suggest you visit more than just 2 regions because they all have something to offer.
|
||||
on’t try to cover it all, it isn’t possible to do so. Instead, pick a few destinations and spend some quality time in each place that you visit so that you don’t feel rushed or drained out.",
|
||||
'author' => 'Jhigu CMS Travel & Tour',
|
||||
'summary' => 'Algarve’s Benagil Cave beach is one of those extra special ones. The only way in is by kayaking or paddle boating, which obviously adds a more elusive touch to this already amazing place',
|
||||
'published_date' => Carbon::now(),
|
||||
'image' => 'b2.jpg',
|
||||
'slug' => "Slug for blog",
|
||||
]
|
||||
];
|
||||
foreach ($blogs as $blog) {
|
||||
$cmsblog = Blog::create([
|
||||
'uuid' => Str::uuid(),
|
||||
'title' => $blog['title'],
|
||||
'content' => $blog['content'],
|
||||
'author' => $blog['author'],
|
||||
'summary' => $blog['summary'],
|
||||
'published_date' => $blog['published_date'],
|
||||
'slug' => $blog['slug'],
|
||||
]);
|
||||
|
||||
// Add image to the created banner
|
||||
$this->uploadImageForBlog($blog['image'], $cmsblog);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function uploadImageForBlog(string $imageFileName, $cmsbanner)
|
||||
{
|
||||
$seederDirPath = 'Blogs/';
|
||||
|
||||
// Generate a unique filename for the new image
|
||||
$newFileName = Str::uuid() . '.jpg';
|
||||
|
||||
// Storage path for the new image
|
||||
$storagePath = '/blogs/' . $newFileName;
|
||||
|
||||
// Check if the image exists in the seeder_disk
|
||||
if (Storage::disk('seeder_disk')->exists($seederDirPath . $imageFileName)) {
|
||||
// Copy the image from seeder to public
|
||||
$fileContents = Storage::disk('seeder_disk')->get($seederDirPath . $imageFileName);
|
||||
|
||||
Storage::disk('public_uploads')->put($storagePath, $fileContents);
|
||||
|
||||
$cmsbanner->image = $newFileName;
|
||||
$cmsbanner->image_path = $storagePath;
|
||||
$cmsbanner->save();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
0
Modules/Blog/lang/.gitkeep
Normal file
0
Modules/Blog/lang/.gitkeep
Normal file
11
Modules/Blog/module.json
Normal file
11
Modules/Blog/module.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Blog",
|
||||
"alias": "blog",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"priority": 0,
|
||||
"providers": [
|
||||
"Modules\\Blog\\app\\Providers\\BlogServiceProvider"
|
||||
],
|
||||
"files": []
|
||||
}
|
15
Modules/Blog/package.json
Normal file
15
Modules/Blog/package.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^1.1.2",
|
||||
"laravel-vite-plugin": "^0.7.5",
|
||||
"sass": "^1.69.5",
|
||||
"postcss": "^8.3.7",
|
||||
"vite": "^4.0.0"
|
||||
}
|
||||
}
|
0
Modules/Blog/resources/assets/.gitkeep
Normal file
0
Modules/Blog/resources/assets/.gitkeep
Normal file
0
Modules/Blog/resources/assets/js/app.js
Normal file
0
Modules/Blog/resources/assets/js/app.js
Normal file
0
Modules/Blog/resources/assets/sass/app.scss
Normal file
0
Modules/Blog/resources/assets/sass/app.scss
Normal file
0
Modules/Blog/resources/views/.gitkeep
Normal file
0
Modules/Blog/resources/views/.gitkeep
Normal file
38
Modules/Blog/resources/views/create.blade.php
Normal file
38
Modules/Blog/resources/views/create.blade.php
Normal file
@@ -0,0 +1,38 @@
|
||||
@extends('admin::layouts.master')
|
||||
|
||||
@section('title')
|
||||
Create Blog
|
||||
@endsection
|
||||
|
||||
@section('breadcrumb')
|
||||
@php
|
||||
$breadcrumbData = [
|
||||
[
|
||||
'title' => 'Blog',
|
||||
'link' => 'null',
|
||||
],
|
||||
[
|
||||
'title' => 'Dashboard',
|
||||
'link' => route('dashboard'),
|
||||
],
|
||||
[
|
||||
'title' => 'Blogs',
|
||||
'link' => null,
|
||||
],
|
||||
[
|
||||
'title' => 'Add Blog',
|
||||
'link' => null,
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
@include('admin::layouts.partials.breadcrumb', $breadcrumbData)
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
<form action="{{ route('cms.blogs.store') }}" method="POST" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@include('blog::partial.form')
|
||||
</form>
|
||||
|
||||
@endsection
|
43
Modules/Blog/resources/views/edit.blade.php
Normal file
43
Modules/Blog/resources/views/edit.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
@extends('admin::layouts.master')
|
||||
|
||||
@section('title')
|
||||
Update Blog
|
||||
@endsection
|
||||
|
||||
@section('breadcrumb')
|
||||
@php
|
||||
$breadcrumbData = [
|
||||
[
|
||||
'title' => 'Blog',
|
||||
'link' => 'null',
|
||||
],
|
||||
[
|
||||
'title' => 'Dashboard',
|
||||
'link' => route('dashboard'),
|
||||
],
|
||||
[
|
||||
'title' => 'Blogs',
|
||||
'link' => null,
|
||||
],
|
||||
[
|
||||
'title' => 'Update Blog',
|
||||
'link' => null,
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
@include('admin::layouts.partials.breadcrumb', $breadcrumbData)
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="card-body">
|
||||
<form action="{{ route('cms.blogs.update', ['uuid' => $blog->uuid]) }}" method="POST"
|
||||
enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
@include('blog::partial.form')
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@endsection
|
138
Modules/Blog/resources/views/index.blade.php
Normal file
138
Modules/Blog/resources/views/index.blade.php
Normal file
@@ -0,0 +1,138 @@
|
||||
@extends('admin::layouts.master')
|
||||
|
||||
@section('title')
|
||||
Blog
|
||||
@endsection
|
||||
|
||||
@section('breadcrumb')
|
||||
@php
|
||||
$breadcrumbData = [
|
||||
[
|
||||
'title' => 'Blog',
|
||||
'link' => 'null',
|
||||
],
|
||||
[
|
||||
'title' => 'Dashboard',
|
||||
'link' => route('dashboard'),
|
||||
],
|
||||
[
|
||||
'title' => 'Blogs',
|
||||
'link' => null,
|
||||
],
|
||||
];
|
||||
@endphp
|
||||
@include('admin::layouts.partials.breadcrumb', $breadcrumbData)
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h4 class="card-header">List of Blog</h4>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="flex-column flex-md-row">
|
||||
<div class="dt-action-buttons text-end pt-3 px-3">
|
||||
<div class="dt-buttons btn-group flex-wrap">
|
||||
<a href="{{ route('cms.blogs.create') }}"
|
||||
class="btn btn-secondary create-new btn-primary d-none d-sm-inline-block text-white">
|
||||
<i class="bx bx-plus me-sm-1"></i>
|
||||
Add New
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-datatable table-responsive">
|
||||
<table class="datatables-users table border-top">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>S.N</th>
|
||||
<th>Title With Image</th>
|
||||
<th>Author</th>
|
||||
<th>Published Date</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="table-border-bottom-0">
|
||||
@if (count($blogs) > 0)
|
||||
@foreach ($blogs ?? [] as $blog)
|
||||
<tr>
|
||||
<td>
|
||||
#{{ $loop->iteration }}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center me-3">
|
||||
<img src="{{ asset($blog->image_path ? 'storage/uploads/' . $blog->image_path : 'backend/uploads/images/no-Image.jpg') }}"
|
||||
alt="Image" class="rounded me-3" height="40" width="60"
|
||||
style="object-fit: cover">
|
||||
<div class="card-title mb-0 px-3">
|
||||
<h6 class="mb-0">{{ $blog->title }}</h6>
|
||||
<small class="text-muted">{{ Str::limit($blog->summary, 80) }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ $blog->author }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $blog->published_date->toFormattedDateString() }}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-label-{{ $blog->status == 'active' ? 'success' : 'danger' }}">
|
||||
{{ $blog->status }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="dropdown">
|
||||
<button type="button" class="btn p-0 dropdown-toggle hide-arrow"
|
||||
data-bs-toggle="dropdown">
|
||||
<i class="bx bx-dots-vertical-rounded"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item"
|
||||
href="{{ route('cms.blogs.edit', ['uuid' => $blog->uuid]) }}"><i
|
||||
class="bx bx-edit-alt me-1"></i>
|
||||
Edit</a>
|
||||
|
||||
|
||||
<form method="POST"
|
||||
action="{{ route('cms.blogs.delete', ['uuid' => $blog->uuid]) }}"
|
||||
id="deleteForm_{{ $blog->uuid }}" class="dropdown-item">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="border-0 bg-transparent deleteBtn"
|
||||
style="color:inherit"><i class="bx bx-trash me-1"></i> Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@else
|
||||
<tr>
|
||||
<td colspan="6">No record found.</td>
|
||||
</tr>
|
||||
@endif
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="px-3">
|
||||
{{ $blogs->links('admin::layouts.partials.pagination') }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
{{-- style --}}
|
||||
@push('required-styles')
|
||||
@include('admin::vendor.dataTables.style')
|
||||
@endpush
|
||||
|
||||
{{-- script --}}
|
||||
@push('required-scripts')
|
||||
@include('admin::vendor.dataTables.script')
|
||||
@endpush
|
29
Modules/Blog/resources/views/layouts/master.blade.php
Normal file
29
Modules/Blog/resources/views/layouts/master.blade.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
|
||||
<title>Blog Module - {{ config('app.name', 'Laravel') }}</title>
|
||||
|
||||
<meta name="description" content="{{ $description ?? '' }}">
|
||||
<meta name="keywords" content="{{ $keywords ?? '' }}">
|
||||
<meta name="author" content="{{ $author ?? '' }}">
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
||||
|
||||
{{-- Vite CSS --}}
|
||||
{{-- {{ module_vite('build-blog', 'resources/assets/sass/app.scss') }} --}}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@yield('content')
|
||||
|
||||
{{-- Vite JS --}}
|
||||
{{-- {{ module_vite('build-blog', 'resources/assets/js/app.js') }} --}}
|
||||
</body>
|
@@ -0,0 +1,43 @@
|
||||
<div class="flex items-center justify-between border-t border-gray-200 bg-white px-4 py-3 sm:px-6">
|
||||
<div class="flex flex-1 justify-between sm:hidden">
|
||||
<a href="#" class="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50">Previous</a>
|
||||
<a href="#" class="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50">Next</a>
|
||||
</div>
|
||||
<div class="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-700">
|
||||
Showing
|
||||
<span class="font-medium">1</span>
|
||||
to
|
||||
<span class="font-medium">10</span>
|
||||
of
|
||||
<span class="font-medium">97</span>
|
||||
results
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<nav class="isolate inline-flex -space-x-px rounded-md shadow-sm" aria-label="Pagination">
|
||||
<a href="#" class="relative inline-flex items-center rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0">
|
||||
<span class="sr-only">Previous</span>
|
||||
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
<!-- Current: "z-10 bg-indigo-600 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600", Default: "text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:outline-offset-0" -->
|
||||
<a href="#" aria-current="page" class="relative z-10 inline-flex items-center bg-indigo-600 px-4 py-2 text-sm font-semibold text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">1</a>
|
||||
<a href="#" class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0">2</a>
|
||||
<a href="#" class="relative hidden items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 md:inline-flex">3</a>
|
||||
<span class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-700 ring-1 ring-inset ring-gray-300 focus:outline-offset-0">...</span>
|
||||
<a href="#" class="relative hidden items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0 md:inline-flex">8</a>
|
||||
<a href="#" class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0">9</a>
|
||||
<a href="#" class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0">10</a>
|
||||
<a href="#" class="relative inline-flex items-center rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20 focus:outline-offset-0">
|
||||
<span class="sr-only">Next</span>
|
||||
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path fill-rule="evenodd" d="M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
132
Modules/Blog/resources/views/partial/form.blade.php
Normal file
132
Modules/Blog/resources/views/partial/form.blade.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<div class="row">
|
||||
<div class="col-8">
|
||||
<div class="card mb-4">
|
||||
<h5 class="card-header">Blog Details</h5>
|
||||
<hr class="my-0">
|
||||
<div class="card-body">
|
||||
<div class="row" x-data="generateSlug()" x-init="title = '{{ addslashes(old('title', $blog->title ?? '')) }}'; slug = '{{ addslashes(old('slug', $blog->slug ?? '')) }}'">
|
||||
<div class="mb-3 fv-plugins-icon-container">
|
||||
<label class="form-label" for="basic-default-name">Title</label>
|
||||
<input type="text" class="form-control" x-model="title" x-on:input="updateSlug()" name="title"
|
||||
value="{{ old('title', $blog->title ?? '') }}"
|
||||
placeholder="e.g. The Ultimate Italy Road Trip: 2 Weeks Itinerary (with Amalfi Coast)"
|
||||
required />
|
||||
</div>
|
||||
<div class="mb-3 col-md-6 fv-plugins-icon-container">
|
||||
<label for="slug" class="form-label">Slug</label>
|
||||
<input class="form-control" type="text" x-model="slug" name="slug" id=""
|
||||
value="{{ old('slug', $blog->slug ?? '') }}" placeholder="e.g:blog slug" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-md-6 fv-plugins-icon-container">
|
||||
<label class="form-label" for="basic-default-company">Author</label>
|
||||
<input type="text" class="form-control" name="author"
|
||||
value="{{ old('author', $blog->author ?? '') }}" placeholder="e.g. Jhigu CMS Travel & Tour"
|
||||
required />
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-md-12">
|
||||
<label for="exampleFormControlTextarea1" class="form-label">Summary</label>
|
||||
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3" name="summary"
|
||||
placeholder="e.g:Write Summary here..." required>{{ old('summary', $blog->summary ?? '') }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-md-12">
|
||||
<label class="form-label" for="basic-default-message">Content</label><br>
|
||||
<textarea name="content" id="advance" placeholder="Description..">{{ !empty($blog) ? $blog->content : '' }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-md-6">
|
||||
<label class="form-label" for="basic-default-company">Published Date</label>
|
||||
<input class="date form-control" type="text" id="flatpickr-datetime" name="published_date"
|
||||
value="{{ old('published_date', $blog->published_date ?? '') }}"
|
||||
placeholder="e.g. 2023-11-25 05:21:42" required />
|
||||
</div>
|
||||
|
||||
<div class="mb-3 col-md-6">
|
||||
<label for="language" class="form-label">Status</label>
|
||||
<div class="position-relative">
|
||||
<select id="language select2Basic" class="select2 form-select select2-hidden-accessible"
|
||||
data-select2-id="language" tabindex="-1" aria-hidden="true" name="status" required>
|
||||
<option value="" data-select2-id="4">Select Status</option>
|
||||
<option value="active"
|
||||
{{ old('status', $blog->status ?? '') == 'active' ? 'selected' : '' }}>
|
||||
Active</option>
|
||||
<option value="inactive"
|
||||
{{ old('status', $blog->status ?? '') == 'inactive' ? 'selected' : '' }}>Inactive
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<button type="submit" class="btn btn-primary me-2">
|
||||
{{ !isset($blog) ? 'Save Changes' : 'Update Changes' }}</button>
|
||||
<button type="reset" class="btn btn-label-secondary">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /Account -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- Right sidebar --}}
|
||||
<div class="col-4">
|
||||
<div class="card mb-4">
|
||||
<h5 class="card-header">Images</h5>
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start align-items-sm-center gap-4">
|
||||
<img src="{{ asset(!empty($blog->image_path) ? 'storage/uploads/' . $blog->image_path : 'backend/uploads/images/no-Image.jpg') }}"
|
||||
alt="blog-image input-file" class="d-block rounded show-image" height="100" width="100" />
|
||||
<div class="button-wrapper">
|
||||
<label for="upload" class="btn btn-primary me-2 mb-4" tabindex="0">
|
||||
<span class="d-none d-sm-block">Upload</span>
|
||||
<i class="bx bx-upload d-block d-sm-none"></i>
|
||||
<input type="file" id="upload" class="input-file" name="image" hidden
|
||||
accept="image/png, image/jpeg" />
|
||||
</label>
|
||||
<button type="button" class="btn btn-label-secondary image-reset mb-4">
|
||||
<i class="bx bx-reset d-block d-sm-none"></i>
|
||||
<span class="d-none d-sm-block">Reset</span>
|
||||
</button>
|
||||
|
||||
<p class="mb-0">Allowed JPG, GIF or PNG. Max size of 3Mb</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-4">
|
||||
<h5 class="card-header">Blog Meta Details</h5>
|
||||
<div class="card-body">
|
||||
<div class="mb-3 col-md-12">
|
||||
<label for="exampleFormControlTextarea1" class="form-label">Meta Title</label>
|
||||
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3" name="meta_title">{{ old('meta_title', $blog->blogMeta->meta_title ?? '') }}</textarea>
|
||||
</div>
|
||||
<div class="mb-3 col-md-12">
|
||||
<label for="exampleFormControlTextarea1" class="form-label">Meta Description</label>
|
||||
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3" name="meta_description">{{ old('meta_description', $blog->blogMeta->meta_description ?? '') }}</textarea>
|
||||
</div>
|
||||
<div class="mb-3 col-md-12">
|
||||
<label for="exampleFormControlTextarea1" class="form-label">Meta Keywords</label>
|
||||
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3" name="meta_keywords">{{ old('meta_keywords', $blog->blogMeta->meta_keywords ?? '') }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{{-- style --}}
|
||||
@push('required-styles')
|
||||
@include('admin::vendor.datePicker.style')
|
||||
@include('admin::vendor.select2.style')
|
||||
@endpush
|
||||
|
||||
{{-- script --}}
|
||||
@push('required-scripts')
|
||||
@include('admin::vendor.tinymce.script')
|
||||
@include('admin::vendor.imageUpload.script')
|
||||
@include('admin::vendor.datePicker.script')
|
||||
@include('admin::vendor.select2.script')
|
||||
@endpush
|
0
Modules/Blog/routes/.gitkeep
Normal file
0
Modules/Blog/routes/.gitkeep
Normal file
19
Modules/Blog/routes/api.php
Normal file
19
Modules/Blog/routes/api.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register API routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider within a group which
|
||||
| is assigned the "api" middleware group. Enjoy building your API!
|
||||
|
|
||||
*/
|
||||
|
||||
Route::middleware(['auth:sanctum'])->prefix('v1')->name('api.')->group(function () {
|
||||
Route::get('blog', fn (Request $request) => $request->user())->name('blog');
|
||||
});
|
40
Modules/Blog/routes/web.php
Normal file
40
Modules/Blog/routes/web.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Modules\Blog\app\Http\Controllers\BlogController;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Web Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register web routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider within a group which
|
||||
| contains the "web" middleware group. Now create something great!
|
||||
|
|
||||
*/
|
||||
|
||||
Route::group(
|
||||
[
|
||||
'prefix' => 'apanel',
|
||||
'middleware' => ['auth'],
|
||||
'as' => 'cms.',
|
||||
],
|
||||
function () {
|
||||
Route::group(
|
||||
[
|
||||
'prefix' => 'cms',
|
||||
'as' => 'blogs.',
|
||||
'controller' => 'BlogController',
|
||||
],
|
||||
function () {
|
||||
Route::get('blogs', 'index')->name('index');
|
||||
Route::get('blogs/create', 'create')->name('create');
|
||||
Route::post('blogs/store', 'store')->name('store');
|
||||
Route::get('blogs/{uuid}/edit', 'edit')->name('edit');
|
||||
Route::put('blogs/{uuid}/update', 'update')->name('update');
|
||||
Route::delete('blogs/{uuid}/delete', 'destroy')->name('delete');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
0
Modules/Blog/tests/Feature/.gitkeep
Normal file
0
Modules/Blog/tests/Feature/.gitkeep
Normal file
0
Modules/Blog/tests/Unit/.gitkeep
Normal file
0
Modules/Blog/tests/Unit/.gitkeep
Normal file
26
Modules/Blog/vite.config.js
Normal file
26
Modules/Blog/vite.config.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import laravel from 'laravel-vite-plugin';
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: '../../public/build-blog',
|
||||
emptyOutDir: true,
|
||||
manifest: true,
|
||||
},
|
||||
plugins: [
|
||||
laravel({
|
||||
publicDirectory: '../../public',
|
||||
buildDirectory: 'build-blog',
|
||||
input: [
|
||||
__dirname + '/resources/assets/sass/app.scss',
|
||||
__dirname + '/resources/assets/js/app.js'
|
||||
],
|
||||
refresh: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
//export const paths = [
|
||||
// 'Modules/$STUDLY_NAME$/resources/assets/sass/app.scss',
|
||||
// 'Modules/$STUDLY_NAME$/resources/assets/js/app.js',
|
||||
//];
|
Reference in New Issue
Block a user