You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

210 lines
7.5 KiB
PHP

<?php
namespace App\Http\Controllers\Administracion;
use App\Http\Controllers\Controller;
use App\Models\Noticia;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
class NoticiaController extends Controller
{
// GET /api/noticias
public function index(Request $request)
{
$perPage = (int) $request->get('per_page', 9);
$query = Noticia::query();
if ($request->filled('publicado')) {
$query->where('publicado', $request->boolean('publicado'));
}
if ($request->filled('categoria')) {
$query->where('categoria', (string) $request->get('categoria'));
}
if ($request->filled('q')) {
$q = trim((string) $request->get('q'));
$query->where(function ($sub) use ($q) {
$sub->where('titulo', 'like', "%{$q}%")
->orWhere('descripcion_corta', 'like', "%{$q}%");
});
}
$data = $query
->orderByDesc('destacado')
->orderByDesc('fecha_publicacion')
->orderByDesc('orden')
->orderByDesc('id')
->paginate($perPage);
return response()->json([
'success' => true,
'data' => $data->items(), // incluye imagen_url por el accessor/appends
'meta' => [
'current_page' => $data->currentPage(),
'last_page' => $data->lastPage(),
'per_page' => $data->perPage(),
'total' => $data->total(),
],
]);
}
// GET /api/noticias/{noticia}
public function show(Noticia $noticia)
{
return response()->json([
'success' => true,
'data' => $noticia, // incluye imagen_url por el accessor/appends
]);
}
// GET /api/noticias-publicas/{noticia} (o la ruta que uses)
public function showPublic(Noticia $noticia)
{
abort_unless($noticia->publicado, 404);
return response()->json([
'success' => true,
'data' => $noticia,
]);
}
// POST /api/noticias (multipart/form-data si viene imagen)
public function store(Request $request)
{
$data = $request->validate([
'titulo' => ['required', 'string', 'max:220'],
'slug' => ['nullable', 'string', 'max:260', 'unique:noticias,slug'],
'descripcion_corta' => ['nullable', 'string', 'max:500'],
'contenido' => ['nullable', 'string'],
'categoria' => ['nullable', 'string', 'max:80'],
'tag_color' => ['nullable', 'string', 'max:30'],
// ✅ dos formas de imagen
'imagen' => ['nullable', 'file', 'mimes:jpg,jpeg,png,webp', 'max:4096'],
'imagen_url' => ['nullable', 'url', 'max:600'],
'link_url' => ['nullable', 'url', 'max:600'],
'link_texto' => ['nullable', 'string', 'max:120'],
'fecha_publicacion' => ['nullable', 'date'],
'publicado' => ['nullable', 'boolean'],
'destacado' => ['nullable', 'boolean'],
'orden' => ['nullable', 'integer'],
]);
// slug por defecto (igual tu modelo lo genera, pero aquí lo dejamos por consistencia)
if (empty($data['slug'])) {
$data['slug'] = Str::slug($data['titulo']);
}
// si viene archivo, manda a storage y prioriza archivo
if ($request->hasFile('imagen')) {
$path = $request->file('imagen')->store('noticias', 'public');
$data['imagen_path'] = $path;
$data['imagen_url'] = null; // ✅ evita conflicto con url externa
} else {
// si viene imagen_url externa, no debe haber imagen_path
$data['imagen_path'] = null;
}
// si publican sin fecha, poner ahora
if (!empty($data['publicado']) && empty($data['fecha_publicacion'])) {
$data['fecha_publicacion'] = now();
}
$noticia = Noticia::create($data);
return response()->json([
'success' => true,
'data' => $noticia->fresh(),
], 201);
}
// PUT/PATCH /api/noticias/{noticia}
public function update(Request $request, Noticia $noticia)
{
$data = $request->validate([
'titulo' => ['sometimes', 'required', 'string', 'max:220'],
'slug' => ['sometimes', 'nullable', 'string', 'max:260', 'unique:noticias,slug,' . $noticia->id],
'descripcion_corta' => ['sometimes', 'nullable', 'string', 'max:500'],
'contenido' => ['sometimes', 'nullable', 'string'],
'categoria' => ['sometimes', 'nullable', 'string', 'max:80'],
'tag_color' => ['sometimes', 'nullable', 'string', 'max:30'],
// ✅ dos formas de imagen
'imagen' => ['sometimes', 'nullable', 'file', 'mimes:jpg,jpeg,png,webp', 'max:4096'],
'imagen_url' => ['sometimes', 'nullable', 'url', 'max:600'],
'link_url' => ['sometimes', 'nullable', 'url', 'max:600'],
'link_texto' => ['sometimes', 'nullable', 'string', 'max:120'],
'fecha_publicacion' => ['sometimes', 'nullable', 'date'],
'publicado' => ['sometimes', 'boolean'],
'destacado' => ['sometimes', 'boolean'],
'orden' => ['sometimes', 'integer'],
]);
// Si llega imagen archivo, reemplaza la anterior y limpia imagen_url externa
if ($request->hasFile('imagen')) {
if ($noticia->imagen_path && Storage::disk('public')->exists($noticia->imagen_path)) {
Storage::disk('public')->delete($noticia->imagen_path);
}
$path = $request->file('imagen')->store('noticias', 'public');
$data['imagen_path'] = $path;
$data['imagen_url'] = null; // ✅ prioridad archivo
}
// Si llega imagen_url (externa), borra la imagen física anterior y limpia imagen_path
if (array_key_exists('imagen_url', $data) && !empty($data['imagen_url'])) {
if ($noticia->imagen_path && Storage::disk('public')->exists($noticia->imagen_path)) {
Storage::disk('public')->delete($noticia->imagen_path);
}
$data['imagen_path'] = null;
}
// Si explícitamente mandan imagen_url = null (quitar url) y no mandan archivo,
// no tocamos imagen_path (se queda como está). Si quieres que también limpie path,
// dime y lo cambiamos.
// si se marca publicado y no hay fecha, set now
if (
array_key_exists('publicado', $data) &&
$data['publicado'] &&
empty($noticia->fecha_publicacion) &&
empty($data['fecha_publicacion'])
) {
$data['fecha_publicacion'] = now();
}
// si cambian titulo y slug no vino, regenerar slug (opcional)
if (array_key_exists('titulo', $data) && !array_key_exists('slug', $data)) {
$data['slug'] = Str::slug($data['titulo']);
}
$noticia->update($data);
return response()->json([
'success' => true,
'data' => $noticia->fresh(),
]);
}
// DELETE /api/noticias/{noticia}
public function destroy(Noticia $noticia)
{
if ($noticia->imagen_path && Storage::disk('public')->exists($noticia->imagen_path)) {
Storage::disk('public')->delete($noticia->imagen_path);
}
$noticia->delete();
return response()->json([
'success' => true,
'message' => 'Noticia eliminada correctamente',
]);
}
}