Ayuda con ordenamiento de tabla en Laravel

  • Autor Autor Angel Javier
  • Fecha de inicio Fecha de inicio
Angel Javier

Angel Javier

Ómicron
Verificado
Verificación en dos pasos activada
Verificado por Whatsapp
¡Ha verificado su Paypal!
Verificado por Binance
Tengo una tabla en mi vista de Laravel.
Cuando la ordeno por fecha (más reciente a más antiguo) me ordena desde el primero de julio, hacia abajo
1720188924798.webp


¿No debería ser desde el 4 de julio hacia atrás?
Y Cuando ordeno al revez, también me muestra mayo, al revez y luego junio
1720189008144.webp


Intenté ordenarlos desde mi controlador, pero no me funciona, de igual manera en mi vista he realizado varios cambios y nada.

¿Que puedo revisar para que me ordene de manera correcta?

Si ordeno decendente, debe mostrarme
Julio 4, 3,2 1
Y ascendente: mayo 31, Junio 1, ect

Código de mi vista:

PHP:
@extends('layouts.admin')
@section('content')
<div class="card">
    <div class="card-body">
        <div class="container-fluid">
            <div class="row">
                <div class="col-lg-2 col-sm-12 col-md-2">
                    <label for="">{{ trans('global.from') }}</label>
                    <input type="date" class="form-control" name="from">
                </div>
                <div class="col-lg-2 col-sm-12 col-md-2">
                    <label for="">{{ trans('global.to') }}</label>
                    <input type="date" class="form-control" name="to">
                </div>
                <div class="col-lg-2 col-sm-12 col-md-2">
                    <label for="">{{ trans('global.employee_name') }}</label>
                    <input type="text" class="form-control" name="employee_name">
                </div>
                <div class="col-lg-6 col-sm-12 col-md-6">
                    <button class="btn btn-primary mt-4 filter">{{ trans('global.filter') }}</button>
                    <button class="btn btn-danger mt-4 reset-filters-btn">{{ trans('global.reset_filter') }}</button>
                    <a href="{{ route('admin.attendance.excuse') }}" class="btn btn-warning mt-4">{{ trans('global.add_absent_excuse') }}</a>
                    <button class="export-pdf btn mt-4 btn-success">{{ trans('global.export_pdf') }}</button>
                    @can('empleado_delete')
                    <button class="ver-pdf btn btn-info mt-4">Ver PDF</button>
                    @endcan
                </div>
                    

            </div>
        </div>

        <div class="table-responsive">
            <table class="table table-bordered table-striped table-hover datatable">
                <thead>
                    <tr>
                        <th>{{ trans('global.employee_name') }}</th>
                        <th>{{ trans('global.date') }}</th>
                        <th>{{ trans('global.entrance') }}</th>
                        <th>{{ trans('global.lunch_departure') }}</th>
                        <th>{{ trans('global.lunch_entry') }}</th>
                        <th>{{ trans('global.exit') }}</th>
                        <th>{{ trans('global.hours_worked') }}</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </div>
    </div>
</div>

<!-- Modal para mostrar el loader -->
<div class="modal fade" id="loadingModal" tabindex="-1" role="dialog" aria-labelledby="loadingModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
        <div class="modal-content">
            <div class="modal-body text-center">
                <div class="spinner-border text-primary" role="status">
                    <span class="sr-only">Loading...</span>
                </div>
                <p class="mt-2">Exportando PDF...</p>
            </div>
        </div>
    </div>
</div>

@endsection

@section('scripts')
@parent
<script>
    $(document).ready(function() {
        $('.export-pdf').on('click', function(e) {
            e.preventDefault();

            // Mostrar alerta de Bootstrap "Enviando Planilla, por favor espera"
            showAlert('Enviando Planilla, por favor espera', 'alert-warning');

            var params = $.param({
                from: $('input[name="from"]').val(),
                to: $('input[name="to"]').val(),
                employee_name: $('input[name="employee_name"]').val()
            });

            var url = '{{ route("admin.attendance.downloadPdf") }}?' + params;

            // Redireccionar a la URL de descarga del PDF para enviar por correo
            window.location.href = url;
        });

        $('.ver-pdf').on('click', function(e) {
            e.preventDefault();

            // Mostrar alerta de Bootstrap "Generando PDF, por favor espera"
            showAlert('Generando PDF, por favor espera', 'alert-warning');

            var params = $.param({
                from: $('input[name="from"]').val(),
                to: $('input[name="to"]').val(),
                employee_name: $('input[name="employee_name"]').val(),
                view: true
            });

            var url = '{{ route("admin.attendance.downloadPdf") }}?' + params;

            // Redireccionar a la URL de visualización del PDF
            window.open(url, '_blank');
        });

        var table = $('.datatable').DataTable({
            processing: true,
            serverSide: true,
            ajax: {
                url: '{{ route("admin.attendance.fetchAttendance") }}',
                data: function(d) {
                    // Agregar valores de filtro a la solicitud AJAX
                    d.from = $('input[name="from"]').val();
                    d.to = $('input[name="to"]').val();
                    d.employee_name = $('input[name="employee_name"]').val();
                }
            },
            columns: [
                { data: 'employee_name', name: 'employee_name' },
                { data: 'date', name: 'date' },
                { data: 'entrance', name: 'entrance' },
                { data: 'lunch_departure', name: 'lunch_departure' },
                { data: 'lunch_entry', name: 'lunch_entry' },
                { data: 'exit', name: 'exit' },
                { data: 'hours_worked', name: 'hours_worked' }
            ],
            dom: 'tip',
            columnDefs: [
                {
                    targets: 0,
                    orderable: true,
                    searchable: true,
                    className: 'dt-body-left'
                }
            ],
            initComplete: function(settings, json) {
                var urlParams = new URLSearchParams(window.location.search);
                var planillaEnviada = urlParams.get('planilla_enviada');

                if (planillaEnviada === 'true') {
                    showAlert('Planilla enviada con éxito', 'alert-success');
                }
            }
        });

        $(document).on('click', '.filter', function(e) {
            e.preventDefault();
            table.draw();
        });

        $(document).on('click', '.reset-filters-btn', function(e) {
            e.preventDefault();

            $('input[name="from"]').val('');
            $('input[name="to"]').val('');
            $('input[name="employee_name"]').val('');
            table.draw();
        });

        function showAlert(message, alertType) {
            var alertDiv = '<div class="alert ' + alertType + ' alert-dismissible fade show" role="alert">';
            alertDiv += message;
            alertDiv += '<button type="button" class="close" data-dismiss="alert" aria-label="Close">';
            alertDiv += '<span aria-hidden="true">&times;</span>';
            alertDiv += '</button>';
            alertDiv += '</div>';

            $('.card-body').prepend(alertDiv);
        }
    });
</script>

@endsection
 
Depende d ké stack estás usando para hacer el sort. D hecho mostrar el view no es d ninguna ayuda salvo ke uses JS para ordenar los datos mostrados.

Ayudaría + ver el modelo o el controller asociado. En todo caso ORM hace fácil obtener los resultados, simplemente agregas un:
...->orderBy(column_name, criteria*)
a la colección d datos respectiva.

* opcional, puede ser desc/asc este último se aplica by default
 
Depende d ké stack estás usando para hacer el sort. D hecho mostrar el view no es d ninguna ayuda salvo ke uses JS para ordenar los datos mostrados.

Ayudaría + ver el modelo o el controller asociado. En todo caso ORM hace fácil obtener los resultados, simplemente agregas un:
...->orderBy(column_name, criteria*)
a la colección d datos respectiva.

* opcional, puede ser desc/asc este último se aplica by default
Lo intenté y no me funcionó, que fue lo más raro.

En un rato subo el controller, ya que ando fuera de casa.
 
Cual es la cónsula que haces para obtener el resultado mostrado en la tabla?, ahí esta el problema, en el back. No en el front.

Es en el orderBy , no lo esta tomando como fecha o esta mal formateado.
 
Aquí el código de mi controlador.
Ya le tengo el order by, pero no me está funcionando.
PHP:
<?php

namespace App\Http\Controllers;

use DateTime;
use ZipArchive;
use DateInterval;
use App\Models\POSTLD_CLCK;
use App\Models\EmployeeList;
use Illuminate\Http\Request;
use Barryvdh\DomPDF\Facade\Pdf;
use App\Exports\AttendanceExport;
use App\Models\PostLdClckDetails;
use Illuminate\Support\Facades\Auth;
use Maatwebsite\Excel\Facades\Excel;
use App\Models\EmployeeAbsenseExcuse;
use Illuminate\Support\Facades\Storage;
use Yajra\DataTables\Facades\DataTables;
use Illuminate\Support\Facades\Mail;
use App\Mail\AttendancePdfEmail;

class AttendanceController extends Controller
{
    // Muestra la vista principal de la asistencia
    public function index()
    {
        return view('attendance.index');
    }

    // Obtiene los datos de asistencia y los muestra en una tabla con DataTables
    public function fetchAttendance(Request $request)
    {
        $dataTableData = $this->fetchAttendanceData($request);

        return DataTables::of(collect($dataTableData))
                ->addColumn('employee_name', fn($data) => $data['employee_name'])
                ->addColumn('date', fn($data) => $data['BusinessDate'])
                ->addColumn('entrance', fn($data) => $data['entrance'])
                ->addColumn('exit', fn($data) => $data['exit'])
                ->addColumn('lunch_departure', function($data) {
                    return $data['lunch_departure'] > 30 ? '<span style="color:red">' . $data['lunch_departure'] . '</span>' : $data['lunch_departure'];
                })
                ->addColumn('lunch_entry', function($data) {
                    return $data['lunch_entry'] > 30 ? '<span style="color:red">' . $data['lunch_entry'] . '</span>' : $data['lunch_entry'];
                })
                ->addColumn('hours_worked', fn($data) => $data['hours_worked'])
                ->addColumn('action', function($data){
                    if($data['entrance'] == 'Ausente')
                    {
                        return  $data['excuse'];
                    }
                    else
                        return '<a href="'.route('admin.attendance.edit',['EntryDate' => $data['EntryDate'], 'EmployeeID' => $data['employee_id']]).'" class="text-danger"></a>';
                })
                ->rawColumns(['lunch_departure', 'lunch_entry','action'])
                ->make(true);
    }

    // Obtiene los datos de asistencia de la base de datos
public function fetchAttendanceData($request, $pdfData = false)
{
    $query = POSTLD_CLCK::select([
        'EmployeeList.FullName as employee_name',
        'EmployeeList.EmployeeID as employee_id',
        'BusinessDate',
        'Punch_Type',
        'Punch_TimeStamp',
        'Modified_TimeStamp',
        'Modified_User_Id'
    ])
    ->join('EmployeeList', 'EmployeeList.EmployeeID', '=', 'POSTLD_CLCK.EmployeeID')
    ->orderBy('BusinessDate', 'asc');  // Ordenar por fecha descendente

    if ($request->filled('from')) {
        $query->whereDate('BusinessDate', '>=', $request->from);
    } else {
        $query->whereDate('BusinessDate', '>=', now()->subDays(45)->toDateString());
    }
    if ($request->filled('to')) {
        $query->whereDate('BusinessDate', '<=', $request->to);
    }
    if ($request->filled('employee_name')) {
        $employeeName = $request->employee_name;
        $query->where('EmployeeList.FullName', 'like', '%' . $employeeName . '%');
    }

    $attendances = $query->get()->groupBy(['employee_name', 'BusinessDate']);

    $dataTableData = [];
    foreach ($attendances as $employeeName => $recordsByDate) {
        foreach ($recordsByDate as $date => $records) {
            $recordData = [
                'employee_id' => $records[0]->employee_id,
                'employee_name' => $employeeName,
                'EntryDate' => date('Y-m-d', strtotime($date)),
                'BusinessDate' => date('F j, Y', strtotime($date)),
                'Modified_User_Id' => $records[0]->Modified_User_Id,
                'entrance' => '',
                'exit' => '',
                'lunch_departure' => '',
                'lunch_entry' => '',
                'hours_worked' => 0,
                'excuse' => '',
                'modified' => []
            ];

            foreach ($records as $record) {
                $timestamp = $record->Modified_User_Id ? $record->Modified_TimeStamp : $record->Punch_TimeStamp;

                switch ($record->Punch_Type) {
                    case 'II':
                        $recordData['entrance'] = !is_null($timestamp) ? date('H:i:s', strtotime($timestamp)) : '';
                        break;
                    case 'BI':
                        $recordData['lunch_entry'] = !is_null($timestamp) ? date('H:i:s', strtotime($timestamp)) : '';
                        break;
                    case 'BO':
                        $recordData['lunch_departure'] = !is_null($timestamp) ? date('H:i:s', strtotime($timestamp)) : '';
                        break;
                    case 'OO':
                        $recordData['exit'] = !is_null($timestamp) ? date('H:i:s', strtotime($timestamp)) : '';
                        break;
                }

                if ($record->Modified_User_Id) {
                    $recordData['modified'][] = $record;
                }
            }

            if ($recordData['entrance'] && $recordData['exit']) {
                $punchTime = new DateTime($recordData['entrance']);
                $modifiedTime = new DateTime($recordData['exit']);

                if ($modifiedTime < $punchTime) {
                    $modifiedTime->modify('+1 day');
                }

                $interval = $punchTime->diff($modifiedTime);

                if ($recordData['lunch_entry'] && $recordData['lunch_departure']) {
                    $lunchEntryTime = new DateTime($recordData['lunch_entry']);
                    $lunchDepartureTime = new DateTime($recordData['lunch_departure']);
                    $lunchInterval = $lunchEntryTime->diff($lunchDepartureTime);

                    $interval->h -= $lunchInterval->h;
                    $interval->i -= $lunchInterval->i;
                    $interval->s -= $lunchInterval->s;

                    if ($interval->i < 0) {
                        $interval->i += 60;
                        $interval->h--;
                    }
                    if ($interval->s < 0) {
                        $interval->s += 60;
                        $interval->i--;
                    }
                }

                $recordData['hours_worked'] = $this->formatInterval($interval);

                if ($recordData['lunch_entry'] && $recordData['lunch_departure']) {
                    $lunchEntryTime = new DateTime($recordData['lunch_entry']);
                    $lunchDepartureTime = new DateTime($recordData['lunch_departure']);
                    $lunchInterval = $lunchEntryTime->diff($lunchDepartureTime);

                    if ($lunchInterval->i > 30) {
                        $recordData['lunch_entry'] = '<span style="color:red">' . $recordData['lunch_entry'] . '</span>';
                        $recordData['lunch_departure'] = '<span style="color:red">' . $recordData['lunch_departure'] . '</span>';
                    }
                }
            }

            if ($pdfData) {
                $dataTableData[$employeeName][] = $recordData;
            } else {
                $dataTableData[] = $recordData;
            }
        }
    }

    $absenseData = EmployeeAbsenseExcuse::join('EmployeeList', 'EmployeeList.EmployeeID', '=', 'employee_absence_excuse.employee_id');

    if ($request->filled('from')) {
        $absenseData = $absenseData->whereDate('date', '>=', $request->from);
    } else {
        $absenseData = $absenseData->whereDate('date', '>=', now()->subDays(45)->toDateString());
    }
    if ($request->filled('to')) {
        $absenseData = $absenseData->whereDate('date', '<=', $request->to);
    }
    if ($request->filled('employee_name')) {
        $employeeName = $request->employee_name;
        $absenseData = $absenseData->where('EmployeeList.FullName', 'like', '%' . $employeeName . '%');
    }
    $absenseData = $absenseData->get();

    if (!empty($absenseData)) {
        foreach ($absenseData as $value) {
            $recordData = [
                'employee_id' => $value->employee_id,
                'employee_name' => $value->employee->FullName,
                'EntryDate' => 'Ausente',
                'BusinessDate' => date('F j, Y', strtotime($value->date)),
                'Modified_User_Id' => 'Ausente',
                'entrance' => $value->excuse,
                'exit' => $value->excuse,
                'lunch_departure' => $value->excuse,
                'lunch_entry' => $value->excuse,
                'hours_worked' => 0,
                'excuse' => $value->authorized_person,
                'modified' => []
            ];

            if ($pdfData) {
                $dataTableData[$value->employee->FullName][] = $recordData;
            } else {
                $dataTableData[] = $recordData;
            }
        }
    }

    return $dataTableData;
}

    // Formatea el intervalo de tiempo en una cadena legible por humanos
    function formatInterval($interval) {
        $parts = [];

        if ($interval->d > 0) {
            $parts[] = $interval->d . ' día' . ($interval->d > 1 ? 's' : '');
        }
        if ($interval->h > 0) {
            $parts[] = $interval->h . ' hora' . ($interval->h > 1 ? 's' : '');
        }
        if ($interval->i > 0) {
            $parts[] = $interval->i . ' minuto' . ($interval->i > 1 ? 's' : '');
        }
        if ($interval->s > 0) {
            $parts[] = $interval->s . ' segundos' . ($interval->s > 1 ? 's' : '');
        }

        return implode(', ', $parts);
    }

public function downloadPdf(Request $request)
{
    // Recoger las fechas del request
    $from = $request->input('from');
    $to = $request->input('to');

    // Obtener los datos de la tabla de asistencia
    $dataTableData = $this->fetchAttendanceData($request, true);
    $userName = Auth::user()->name;

    // Renderizar el HTML para la asistencia actual, incluyendo las fechas
    $htmlContent = view('attendance.pdf', [
        'data' => $dataTableData,
        'from' => $from,
        'to' => $to
    ])->render();
    $decodedHtmlContent = html_entity_decode($htmlContent);

    // Crear el PDF
    $pdf = PDF::loadHTML($decodedHtmlContent);
    $pdf->getDomPDF()->set_option('isPhpEnabled', true);
    $pdf->getDomPDF()->set_option('isHtml5ParserEnabled', true);

    // Si el request tiene 'view', mostrar el PDF en el navegador
    if ($request->has('view')) {
        return $pdf->stream('attendance_report.pdf');
    }

    // Guardar el PDF en memoria
    $pdfContent = $pdf->output();

    // Enviar el PDF por correo electrónico
    Mail::to('soporte@test.com')->send(new AttendancePdfEmail($pdfContent, $userName));

    return redirect()->back()->with('success', 'PDF enviado por correo electrónico correctamente.');
}


    public function excuse()
{
    // Obtener solo los empleados activos (status = 0)
    $employees = EmployeeList::where('status', 65)->select('EmployeeID', 'FullName')->pluck('FullName', 'EmployeeID')->toArray();

    return view('attendance.excuse', compact('employees'));
}

public function excuseStore(Request $request)
{
    $user = auth()->user()->name;

    // Validación de los datos del request
    $request->validate([
        'employee_id' => 'required|exists:EmployeeList,EmployeeID',
        'date' => 'required|date',
        'excuse' => 'required|string|min:3'
    ]);

    // Verificar si ya existe una excusa para el mismo empleado y fecha
    $existingExcuse = EmployeeAbsenseExcuse::where('employee_id', $request->employee_id)
                                           ->whereDate('date', $request->date)
                                           ->first();

    if ($existingExcuse) {
        return redirect()->back()->with('error', 'Ya existe una excusa para este empleado en la fecha seleccionada.');
    }

    // Verificar si ya existen registros de asistencia para el mismo empleado y fecha
    $checkEntryExistance = POSTLD_CLCK::whereDate('BusinessDate', $request->date)
                                      ->where('EmployeeID', $request->employee_id)
                                      ->exists();

    if ($checkEntryExistance) {
        return redirect()->back()->with('error', 'Ya existen registros de asistencia para este empleado en la fecha seleccionada.');
    }

    // Crear y guardar la nueva excusa
    $absence = new EmployeeAbsenseExcuse();
    $absence->employee_id = $request->employee_id;
    $absence->date = $request->date;
    $absence->excuse = $request->excuse;
    $absence->authorized_person = $user;
    $absence->save();

    return redirect()->route('admin.attendance.index')->with('success', 'Excusa añadida con éxito.');
}


public function getAvailableEmployees($date)
{
    $employeesWithAttendance = POSTLD_CLCK::whereDate('BusinessDate', $date)
                                          ->pluck('EmployeeID')
                                          ->toArray();

    $employeesWithExcuses = EmployeeAbsenseExcuse::whereDate('date', $date)
                                                 ->pluck('employee_id')
                                                 ->toArray();

    $excludedEmployeeIds = array_merge($employeesWithAttendance, $employeesWithExcuses);

    $employees = EmployeeList::where('status', 65)
                             ->whereNotIn('EmployeeID', $excludedEmployeeIds)
                             ->select('EmployeeID', 'FullName')
                             ->get()
                             ->map(function($employee) {
                                 return [
                                     'id' => $employee->EmployeeID,
                                     'name' => $employee->FullName
                                 ];
                             });

    return response()->json($employees);
}


}
 
Todo esta muy desordenado tienes que refactorizar en servicios/helpers o como sea tu patrón de diseño
.
->orderBy('BusinessDate', 'asc'); // Ordenar por fecha descendente
ok, CREO que es por que no especificas la tabla. Se confunde con los JOINS

supongo que usas debugBar? bien. Mira el select y como esta ordenando. Ahí confirmaras el problema y por que esta ordenando de manera rara.
 
Última edición:
Todo esta muy desordenado tienes que refactorizar en servicios/helpers o como sea tu patrón de diseño
.
->orderBy('BusinessDate', 'asc'); // Ordenar por fecha descendente
ok, CREO que es por que no especificas la tabla. Se confunde con los JOINS

supongo que usas debugBar? bien. Mira el select y como esta ordenando. Ahí confirmaras el problema y por que esta ordenando de manera rara.
Y sí, en debugbar te sale todo y como se ejecuta la consulta al final porque el problema puede estar en javascript, en el controlador o incluso hasta en el modelo.
 
ordenalo bajo created_at y updated_at, estás muy desordenado
 
Al final lograste resolverlo?
 
Atrás
Arriba