😍 Dự án Laravel bán hàng martfury dùng Botble quá đỉnh (ok)
Api sử dụng
<?php
use Botble\Blog\Models\Category;
use Botble\Blog\Models\Post;
use Botble\Page\Models\Page;
use Botble\Slug\Facades\SlugHelper;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Botble\Slug\Models\Slug;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Botble\Contact\Models\Contact;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/
Route::get('/pages', function (Request $request) {
$posts = Page::where('status', 'published')
->orderBy('created_at', 'desc')
->select('id', 'name', 'created_at') // tuỳ chọn field
->paginate(10); // hoặc ->get() nếu không phân trang
return response()->json([
'data' => $posts,
]);
});
Route::get('/posts', function (Request $request) {
// Lấy ID chuyên mục 'human' từ bảng slugs
$excludedCategoryId = Slug::where('key', 'human')
->where('reference_type', Category::class)
->value('reference_id');
// Nếu không có chuyên mục "human", trả về bài viết bình thường
if (!$excludedCategoryId) {
$posts = Post::where('status', 'published')
->orderBy('created_at', 'desc')
->select('id', 'name', 'description', 'content', 'is_featured', 'image', 'created_at')
->paginate(10);
$posts->getCollection()->transform(function ($post) {
$slug = DB::table('slugs')
->where('reference_type', Post::class)
->where('reference_id', $post->id)
->value('key');
$post->slug = $slug;
return $post;
});
return response()->json(['data' => $posts]);
}
// Lấy danh sách post_id thuộc chuyên mục human
$excludedPostIds = DB::table('post_categories')
->where('category_id', $excludedCategoryId)
->pluck('post_id');
// Lấy các bài viết KHÔNG thuộc chuyên mục đó
$posts = Post::where('status', 'published')
->whereNotIn('id', $excludedPostIds)
->orderBy('created_at', 'desc')
->select('id', 'name', 'description', 'content', 'is_featured', 'image', 'created_at')
->paginate(10);
// Gắn slug cho bài viết
$posts->getCollection()->transform(function ($post) {
$slug = DB::table('slugs')
->where('reference_type', Post::class)
->where('reference_id', $post->id)
->value('key');
$post->slug = $slug;
return $post;
});
return response()->json(['data' => $posts]);
});
Route::get('/pages/{slug}', function ($slug) {
// Tìm slug từ bảng `slugs`
$slugRecord = Slug::where('key', $slug)
->where('reference_type', Page::class)
->first();
if (! $slugRecord) {
return response()->json(['message' => 'Page not found'], 404);
}
// Tìm page từ reference_id
$page = Page::where('id', $slugRecord->reference_id)
->where('status', 'published')
->first();
if (! $page) {
return response()->json(['message' => 'Page not found'], 404);
}
return response()->json([
'data' => [
'id' => $page->id,
'name' => $page->name,
'description' => $page->description,
'slug' => $slug,
'content' => $page->content,
'custom_fields' => get_custom_field($page),
]
]);
});
Route::get('/posts/{slug}/related', function ($slug) {
$slugRecord = Slug::where('key', $slug)
->where('reference_type', Post::class)
->first();
if (! $slugRecord) {
return response()->json(['message' => 'Post not found'], 404);
}
// Lấy bài viết hiện tại và chuyên mục của nó
$post = Post::with('categories')
->where('id', $slugRecord->reference_id)
->where('status', 'published')
->first();
if (! $post) {
return response()->json(['message' => 'Post not found'], 404);
}
// Lấy các ID chuyên mục của bài viết
$categoryIds = $post->categories->pluck('id');
// Tìm bài viết liên quan (cùng chuyên mục, không trùng chính nó)
$relatedPosts = Post::where('status', 'published')
->where('id', '!=', $post->id)
->whereHas('categories', function ($query) use ($categoryIds) {
$query->whereIn('categories.id', $categoryIds);
})
->orderBy('created_at', 'desc')
->take(6)
// ->select('id', 'name', 'slug', 'description', 'image', 'created_at')
->get();
$relatedPosts->transform(function ($post) {
$slug = DB::table('slugs')
->where('reference_type', Post::class)
->where('reference_id', $post->id)
->value('key');
$post->slug = $slug;
return $post;
});
return response()->json([
'data' => $relatedPosts,
]);
});
Route::get('/posts/{slug}', function ($slug) {
// Tìm slug từ bảng `slugs`
$slugRecord = Slug::where('key', $slug)
->where('reference_type', Post::class)
->first();
if (! $slugRecord) {
return response()->json(['message' => 'Page not found'], 404);
}
// Tìm page từ reference_id
$post = Post::where('id', $slugRecord->reference_id)
->where('status', 'published')
->first();
if (! $post) {
return response()->json(['message' => 'Page not found'], 404);
}
return response()->json([
'data' => [
'id' => $post->id,
'name' => $post->name,
'slug' => $slug,
'description' => $post->description,
'is_featured' => $post->is_featured,
'image' => $post->image,
'created_at' => $post->created_at,
'content' => $post->content,
'categories' => $post->categories->map(function ($category) {
return [
'id' => $category->id,
'name' => $category->name,
'slug' => $category->slug,
];
}),
]
]);
});
Route::get('/categories/{slug}/posts', function ($slug, Request $request) {
// Tìm chuyên mục theo slug
$category = DB::table('slugs')
->where('key', $slug)
->where('reference_type', Category::class)
->first();
$slugRecord = DB::table('slugs')
->where('key', $slug)
->where('reference_type', Category::class)
->first();
$categorys = Category::select('name', 'description')
->where('id', $slugRecord->reference_id)
->first();
// Lấy bài viết thuộc category
$categoryId = $category->reference_id;
$posts = Post::where('status', 'published')
->whereHas('categories', function ($query) use ($categoryId) {
$query->where('categories.id', $categoryId);
})
->orderBy('created_at', 'desc')
->select('id', 'name', 'description', 'content', 'is_featured', 'image', 'created_at')
->paginate(10);
// Map slug cho từng bài viết
$posts->getCollection()->transform(function ($post) {
$slug = DB::table('slugs')
->where('reference_type', Post::class)
->where('reference_id', $post->id)
->value('key');
$post->slug = $slug;
if (!$post->custom_fields) {
$post->custom_fields = get_custom_field($post);
}
return $post;
});
return response()->json([
'data' => $posts,
'category' => $categorys
]);
});
// Route::post('/contactforconsultation', function (\Illuminate\Http\Request $request) {
// \Botble\Contact\Models\Contact::create([
// 'name' => $request->input('yourName') ?? $request->input('position'),
// 'email' => $request->input('email'),
// 'phone' => $request->input('phone'),
// 'content' => $request->input('content') ?? $request->input('position'),
// 'address' => $request->input('address') ?? $request->input('cvfilename'),
// 'subject' => $request->input('subject') ?? $request->input('taxcode')
// ]);
// return response()->json(['message' => 'Gửi thành công.']);
// });
Route::post('/contactforconsultation', function (Request $request) {
// Validate the request data (highly recommended)
$request->validate([
'yourName' => 'nullable|string|max:255',
'email' => 'required|email|max:255',
'phone' => 'required|string|max:20',
'content' => 'nullable|string',
'taxcode' => 'nullable|string|max:50',
'companyProfile' => 'nullable|file|mimes:pdf,ppt,pptx,doc,docx,zip|max:5120', // Max 5MB (5120 KB)
'cvfilename' => 'nullable|string|max:255', // Để lấy tên file từ frontend
]);
$companyProfilePath = null;
if ($request->hasFile('companyProfile')) {
$file = $request->file('companyProfile');
// Lưu file vào thư mục 'public/company_profiles'
// 'public' là một disk mặc định trong Laravel, mapping tới storage/app/public
// Đảm bảo bạn đã chạy `php artisan storage:link` để tạo symlink public/storage
$companyProfilePath = $file->store('company_profiles', 'public');
// $companyProfilePath sẽ chứa đường dẫn tương đối từ thư mục lưu trữ, ví dụ: 'company_profiles/abcxyz.pdf'
}
Contact::create([
'name' => $request->input('yourName') ?? $request->input('companyName'), // Thêm companyName nếu bạn gửi từ frontend
'email' => $request->input('email'),
'phone' => $request->input('phone'),
'content' => $request->input('content'), // Giữ nguyên 'content' từ frontend
// Lưu đường dẫn file vào trường 'address' hoặc tạo một cột mới nếu muốn rõ ràng hơn
'address' => $companyProfilePath ? Storage::url($companyProfilePath) : null, // Lưu URL công khai
'subject' => $request->input('subject') ?? $request->input('taxcode')
]);
return response()->json(['message' => 'Gửi thành công.']);
});
Route::post('/humanresource', function (Request $request) {
// Validate the request data (highly recommended)
$request->validate([
'yourName' => 'required|string|max:255', // Đổi nullable thành required
'email' => 'required|email|max:255',
'phone' => 'required|string|max:20',
'subject' => 'nullable|string|max:255', // Từ form: "Ứng tuyển vị trí: ..."
'taxcode' => 'nullable|string|max:50', // Từ form: "Ứng tuyển"
'position' => 'required|string|max:255', // Thêm trường position
'companyProfile' => 'required|file|mimes:pdf,doc,docx,zip|max:5120', // Đổi nullable thành required, mimes chuẩn hơn
'cvfilename' => 'nullable|string|max:255', // Tên file gốc từ frontend
]);
$companyProfilePath = null;
$publicFilePath = null; // Để lưu URL công khai
if ($request->hasFile('companyProfile')) {
$file = $request->file('companyProfile');
// Lưu file vào thư mục 'public/cv_applications' (thay đổi thư mục cho rõ ràng hơn)
$companyProfilePath = $file->store('cv_applications', 'public');
$publicFilePath = Storage::url($companyProfilePath); // Lấy URL công khai
}
// Tạo nội dung cho trường 'content' để dễ đọc trong Botble
$content = "Họ và tên: " . $request->input('yourName') . "\n" .
"Số điện thoại: " . $request->input('phone') . "\n" .
"Email: " . $request->input('email') . "\n" .
"Vị trí ứng tuyển: " . $request->input('position') . "\n";
if ($publicFilePath) {
$content .= "Link CV: " . $publicFilePath . "\n";
}
Contact::create([
'name' => $request->input('yourName'),
'email' => $request->input('email'),
'phone' => $request->input('phone'),
'content' => $content, // Sử dụng content đã tạo
'address' => $publicFilePath, // Lưu trực tiếp URL của CV vào trường address
'subject' => $request->input('subject'),
// Nếu bạn muốn lưu tên file gốc vào một trường khác, bạn cần thêm cột vào DB
// 'cv_original_name' => $request->input('cvfilename'),
]);
return response()->json(['message' => 'Gửi hồ sơ ứng tuyển thành công.']);
});
Source code 1



User: phamkyanh8668@gmail.com Password: 12345678
Hoặc đã tải lên google driver https://drive.google.com/file/d/1drNl9H6GY9OebkuNUPovTikMxxpfpB2M/view?usp=sharing
Sử dụng plugin custom-field để thêm nội dung vào bài viết, chuyên mục, trang ...

Cách cài đặt plugin & theme


Download plugin https://github.com/buzz-space/dev3map-backend/tree/prod/platform/plugins/custom-field rồi giải nén ra vào thư mục

Bước 1: Để active plugin chúng ra dùng lệnh php artisan plugin:publish nó gợi ý ở dưới

Bước 2: Dùng lệnh php artisan cms:plugin:activate sau đó nó sẽ hỏi plugin cần actate

👌 Cần thiết sau khi cài sử dụng composer dump-autoload để cài đặt lại

Bước 3: Sau khi cài xong nó xuất hiện trong back-end như sau.


Bước 4: Hướng dẫn sử dụng get_field trong platform\plugins\custom-field\helpers\front.php để lấy dữ liệu
platform\plugins\custom-field\helpers\front.php
Ví dụ:
Viết api lấy tất cả các trang và sau đó lấy bài viết chi tiết
routes\api.php
— Cách lấy get_field
https://github.com/codezin/beeart_shopwise/tree/main
— Viêt thêm hàm get_page_by_id
có source code để tham khảo thêm cách hàm khác
platform\plugins\blog\helpers\helpers.php
platform\packages\page\helpers\helpers.php
Chú ý: mặc định nó sẽ lấy cái đầu tiên

platform\themes\martfury\layouts\coming-soon.blade.php

— Cách Lấy get_sub_field




Kết quản thật đáng mong đợi

platform\themes\martfury\layouts\coming-soon.blade.php
Hàng loạt các theme để nghiên cứu
'get_field' is deprecated.
C:\xampp82\htdocs\lva3\platform\themes
'get_field' is deprecated. được hướng dẫn sử dụng getMetaData để thay thế
vendor\botble\platform\base\src\Models\Concerns\HasMetadata.php
Có cần thêm HasMetadata vào bộ điều khiển của Trang không?
ChatGPT cho biết:
Không cần thêm HasMetaDatavào c.
Tuy nhiên, bạn cần đảm bảo mô hìnhPagecó sử dụngHasMetaBoxeshoHasMetaDatađ$page->getMetaData().
✅ Kiểm tra cách và bổ sung các đặc điểm đúng
Mở file Pagemod
app/Models/Page.php(platform/packages/page/models/Page.phptùy chọn vào
Đảm bảo có dòng sau trong cl
Giải th
MetaBoxablelà tragetMetaData()vàsetMetaData()vàoBạn không cần thêm bất cứ điều gì vào bộ điều khiển, vì tiếp tục
$page->getMetaData(...)nếu
🔎 Kiểm tra n
Nế
Thì bạn đã có đặc điểm đúng. If failed Call to undefined method, thphải thêm trait MetaBoxablevào m.
Bạn có cần tôi kiểm tra hoặc viết lại toàn bộ Pagemô hình cấu trúc có các trường tùy chỉnh được hỗ trợ không?
Last updated
Was this helpful?

