Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Tailwind Datatables Laravel Package

squipix/tailwind-datatables

View on GitHub
Deep Wiki
Context7

دليل تثبيت Laravel + Vue + Tailwind DataTables

يوفر هذا الدليل تعليمات خطوة بخطوة لإنشاء مشروع Laravel، وإعداد Vue وInertia.js، وتثبيت وتهيئة حزمة squipix/tailwind-datatables.


1. إنشاء مشروع Laravel

أولاً، أنشئ مشروع Laravel جديداً. يُرجى البدء بتطوير مشروعك من هذه الخطوة؛ فهي ضرورية لضمان بداية صحيحة:

laravel new squipix-datatable-vue && cd squipix-datatable-vue && composer require laravel/breeze --dev && touch resources/js/bootstrap.js && php artisan breeze:install vue &&  rm resources/js/app.ts && rm vite.config.ts && rm -rf resources/js/pages
 

إصلاح فوضى ملف package.json

ملف package.json الذي يأتي مع تثبيت Laravel-Vue الجديد يكون نظيفاً ومنظماً. ومع ذلك، عند تشغيل أمر breeze:install vue، يُفسد هذا السقالة البنية الموجودة. يُعالج سكريبت التثبيت لدينا معظم الأضرار الناجمة عن هذه الإضافة، لكنك تحتاج الآن إلى تنظيف ملف package.json الخاص بك لأنه أصبح فوضوياً بسبب أمر breeze:install vue وسيتسبب في تعارضات.

في هذه المرحلة، يجب أن يبدو ملف package.json كما يلي:

{
    "private": true,
    "type": "module",
    "scripts": {
        "build": "vite build",
        "build:ssr": "vite build && vite build --ssr",
        "dev": "vite",
        "format": "prettier --write resources/",
        "format:check": "prettier --check resources/",
        "lint": "eslint . --fix"
    },
    "devDependencies": {
        "[@eslint](https://github.com/eslint)/js": "^9.19.0",
        "[@tailwindcss](https://github.com/tailwindcss)/forms": "^0.5.3",
        "[@tailwindcss](https://github.com/tailwindcss)/postcss": "^4.1.11",
        "[@types](https://github.com/types)/node": "^22.13.5",
        "[@vue](https://github.com/vue)/eslint-config-typescript": "^14.3.0",
        "autoprefixer": "^10.4.12",
        "eslint": "^9.17.0",
        "eslint-config-prettier": "^10.0.1",
        "eslint-plugin-vue": "^9.32.0",
        "postcss": "^8.4.31",
        "prettier": "^3.4.2",
        "prettier-plugin-organize-imports": "^4.1.0",
        "prettier-plugin-tailwindcss": "^0.6.11",
        "typescript-eslint": "^8.23.0",
        "vue-tsc": "^2.2.4"
    },
    "dependencies": {
        "[@inertiajs](https://github.com/inertiajs)/vue3": "^2.0.0",
        "[@vitejs](https://github.com/vitejs)/plugin-vue": "^5.2.1",
        "[@vueuse](https://github.com/vueuse)/core": "^12.8.2",
        "class-variance-authority": "^0.7.1",
        "clsx": "^2.1.1",
        "concurrently": "^9.0.1",
        "laravel-vite-plugin": "^1.0",
        "lucide-vue-next": "^0.468.0",
        "reka-ui": "^2.2.0",
        "tailwind-merge": "^3.2.0",
        "tailwindcss": "^4.1.1",
        "tw-animate-css": "^1.2.5",
        "typescript": "^5.2.2",
        "vite": "^6.2.0",
        "vue": "^3.5.13",
        "ziggy-js": "^2.4.2"
    } 
}

2. تثبيت حزمة Composer

composer require squipix/tailwind-datatables

نشر أصول الحزمة:


composer update

php artisan vendor:publish --tag=tailwind-datatables-views
php artisan vendor:publish --tag=squipix-tailwind-datatables-config 
php artisan vendor:publish --tag=tailwind-datatables-css

بعد ذلك، قم بتحديث محتوى ملف resources/js/app.js كما هو موضح أدناه. يجب أن يكون هذا الملف موجوداً بالفعل.


// resources/js/app.js
import '../css/app.css';
// ********************************************************************
// * DataTables والمكتبات ذات الصلة (الاحتياجات العامة فقط)           *
// ********************************************************************

import jQuery from 'jquery';
window.jQuery = jQuery;
window.$ = jQuery;

import 'datatables.net';
import 'datatables.net-buttons';
import 'datatables.net-buttons/js/buttons.html5.js'; 
import 'datatables.net-buttons/js/buttons.print.js'; 
import 'datatables.net-buttons/js/buttons.colVis.js'; 
import 'datatables.net-responsive';



import JSZip from 'jszip';
import pdfMake from 'pdfmake/build/pdfmake';
import pdfFonts from 'pdfmake/build/vfs_fonts';
window.JSZip = JSZip;
window.pdfMake = pdfMake;
window.pdfMake.vfs = pdfFonts.vfs;

import Swal from 'sweetalert2';
window.Swal = Swal;

import dayjs from 'dayjs';
window.dayjs = dayjs;
import Alpine from 'alpinejs';

import { registerGorlabsDatatablesAlpineComponents } from '../../vendor/squipix/tailwind-datatables/resources/js/crud-datatable';


// ********************************************************************
// * كود تهيئة Inertia.js و Vue الحالي (لا تغيّره)                    *
// ********************************************************************
import { createInertiaApp } from '[@inertiajs](https://github.com/inertiajs)/vue3';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import { createApp, h } from 'vue';
import { ZiggyVue } from 'ziggy-js';

const appName = import.meta.env.VITE_APP_NAME || 'Laravel';

createInertiaApp({
    title: (title) => `${title} - ${appName}`,
    resolve: (name) =>
        resolvePageComponent(
            `./Pages/${name}.vue`,
            import.meta.glob('./Pages/**/*.vue'),
        ),
    setup({ el, App, props, plugin }) {
        return createApp({ render: () => h(App, props) })
            .use(plugin)
            .use(ZiggyVue)
            .mount(el);
    },
    progress: {
        color: '#4B5563',
    },
});
// القسم الجديد المضاف: انتظر تحميل Alpine.js وتسجيل المكونات
// يُطلق مستمع الحدث هذا عند تحميل Alpine.js من CDN وجاهزيته.
registerGorlabsDatatablesAlpineComponents(Alpine); // تسجيل المكونات
Alpine.start();



تحديث محتوى postcss.config.js

export default {
    plugins: {
        '[@tailwindcss](https://github.com/tailwindcss)/postcss': {},
        autoprefixer: {},
    },
};

6. إعدادات Vite

قم بتحديث محتوى ملف vite.config.js كما هو موضح أدناه. يجب أن يكون هذا الملف موجوداً بالفعل.

import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '[@vitejs](https://github.com/vitejs)/plugin-vue';
export default defineConfig({
    plugins: [
        laravel({
            input: [
                'resources/css/app.css',
                'resources/js/app.js'
            ],
            refresh: true,
        }),
        vue({
            template: {
                transformAssetUrls: {
                    base: null,
                    includeAbsolute: false,
                },
            },
        }),
    ],
    build: {
        minify: true, // تفعيل الضغط
        sourcemap: true, // إنشاء sourcemap لتسهيل تصحيح الأخطاء
    },
    server: {
        cors: true,
    },

});




7. تحديث محتوى resources/js/types/global.d.ts

//resources/js/types/globals.d.ts
import { AppPageProps } from '@/types/index';
import type Swal from 'sweetalert2'; // استخدام type import
import type dayjs from 'dayjs';     // استخدام type import
import type Alpine from 'alpinejs';  // استخدام type import
import type * as pdfMake from 'pdfmake/build/pdfmake'; // استخدام type import
import type * as JSZip from 'jszip'; // استخدام type import

// توسيع واجهة ImportMeta لـ Vite...
declare module 'vite/client' {
    interface ImportMetaEnv {
        readonly VITE_APP_NAME: string;
        [key: string]: string | boolean | undefined;
    }

    interface ImportMeta {
        readonly env: ImportMetaEnv;
        readonly glob: <T>(pattern: string) => Record<string, () => Promise<T>>;
    }
}

declare module '[@inertiajs](https://github.com/inertiajs)/core' {
    interface PageProps extends InertiaPageProps, AppPageProps {}
}

declare module 'vue' {
    interface ComponentCustomProperties {
        $inertia: typeof Router;
        $page: Page;
        $headManager: ReturnType<typeof createHeadManager>;
    }
}

declare global {
    interface Window {
        GorlabsDatatables: {
            date: (format: string) => (data: any, type: string, row: any) => string;
            statusBadge: (publishedText: string, draftText: string) => (data: any, type: string, row: any) => string;
            actions: (updateUrlPrefix: string, deleteUrlPrefix: string) => (data: any, type: string, row: any) => string;
            // أضف هنا بقية توابع GorlabsDatatables
        };
        Swal: any; // يمكنك إضافة تعريف مماثل لـ SweetAlert2
        openFormModal: (title: string, url: string, item: any | null) => void; // للدالة العامة أيضاً
    }
}

// ********************************************************************
// * القسم الجديد المضاف: تعريفات كائن Window العامة                   *
// ********************************************************************
declare global {
    interface Window {
        Swal: typeof Swal;
        dayjs: typeof dayjs;
        Alpine: typeof Alpine;
        pdfMake: typeof pdfMake;
        JSZip: typeof JSZip;
        jQuery: JQuery;
        $: JQuery;
        crudDataTable: (config: any) => any;
    }
}

8. ملفات PHP الخلفية

إنشاء ملف Migration

أنشئ ملف migration لإعداد جدول posts واستبدل محتواه بالتالي:

php artisan make:migration create_posts_table

سيكون مسار الملف المُنشأ: database/migrations/YYYY_MM_DD_HHMMSS_create_posts_table.php (حيث YYYY_MM_DD_HHMMSS تتغير بحسب التاريخ والوقت). قم بتحديث محتواه كما يلي:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * تشغيل عمليات الترحيل.
     */
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('content');
            $table->timestamp('published_at')->nullable(); // تاريخ النشر
            $table->boolean('is_published')->default(false); // الحالة (هل تم النشر؟)
            $table->string('status')->default('draft'); // أضيف كمثال
            $table->timestamps();
        });
    }

    /**
     * عكس عمليات الترحيل.
     */
    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

إنشاء النموذج (Model)

أنشئ نموذج Post وملف PostFactory.php واستبدل محتواهما بالتالي:

php artisan make:model Post -f

سيكون مسار الملف المُنشأ: app/Models/Post.php. قم بتحديث محتواه كما يلي:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'content',
        'published_at',
        'is_published',
        'status',
    ];

    protected $casts = [
        'is_published' => 'boolean',
        'published_at' => 'datetime',
    ];
}


يجب أن يكون محتوى PostFactory.php كما يلي:

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * [@extends](https://github.com/extends) \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Post>
 */
class PostFactory extends Factory
{
    /**
     * تعريف الحالة الافتراضية للنموذج.
     *
     * [@return](https://github.com/return) array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'title' => fake()->sentence(rand(3, 8)),
            'content' => fake()->paragraphs(rand(1, 3), true),
            'published_at' => fake()->optional(0.8)->dateTimeThisYear(), // يعطي تاريخاً باحتمال 80%
            'is_published' => fake()->boolean(90), // true باحتمال 90%
            'status' => fake()->randomElement(['draft', 'published', 'archived']),
        ];
    }
}
php artisan make:seeder PostSeeder

محتوى PostSeeder.php:

<?php

namespace Database\Seeders;

use App\Models\Post;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class PostSeeder extends Seeder
{
    /**
     * تشغيل بذور قاعدة البيانات.
     */
    public function run(): void
    {
        // إنشاء 50 منشوراً وهمياً
        Post::factory()->count(50)->create();
    }
}

محتوى DatabaseSeeder.php

<?php

namespace Database\Seeders;

use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Database\Factories\PostFactory;use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * بذر قاعدة بيانات التطبيق.
     */
    public function run(): void
    {
        // User::factory(10)->create();

        User::factory()->create([
            'name' => 'Test User',
            'email' => 'test@example.com',
        ]);
        $this->call([PostSeeder::class,]);
    }
}

إنشاء فئة DataTable

أنشئ فئة PostsDataTable واستبدل محتواها بالتالي:

php artisan datatable:make PostsDataTable --model=Post

سيكون مسار الملف المُنشأ: app/DataTables/PostsDataTable.php. قم بتحديث محتواه كما يلي:

<?php

namespace App\DataTables;

use App\Models\Post;
use Illuminate\Database\Eloquent\Builder as QueryBuilder;
use Yajra\DataTables\EloquentDataTable;
use Yajra\DataTables\Html\

Builder as HtmlBuilder;
use Yajra\DataTables\Html\Button;
use Yajra\DataTables\Html\Column;
use Yajra\DataTables\Services\DataTable;

class PostsDataTable extends DataTable
{
    public function query(Post $model): QueryBuilder
    {
        return $model->newQuery()->select('id', 'title', 'content', 'is_published', 'published_at', 'status', 'created_at', 'updated_at');
    }

    public function dataTable(QueryBuilder $query): EloquentDataTable
    {
        return (new EloquentDataTable($query))
            ->setRowId('id')
            ->addColumn('select_checkbox', function(Post $post) {
                return '';
            })
            ->addColumn('actions', function(Post $post) {
                return '';
            })
            ->editColumn('is_published', function(Post $post) {
                return $post->is_published ? 1 : 0;
            })
            ->editColumn('published_at', function(Post $post) {
                return $post->published_at ? $post->published_at->format('Y-m-d H:i:s') : null;
            })
            ->editColumn('title', function(Post $post) {
                $title = $post->title;
                if (strlen($title) > 24) {
                    return substr($title, 0, 24) . ' ...';
                }
                return $title;
            })
            ->editColumn('content', function(Post $post) {
                $content = $post->content;
                if (strlen($content) > 34) {
                    return substr($content, 0, 34) . ' ...';
                }
                return $content;
            });
    }

    public function html(): HtmlBuilder
    {
        return $this->builder()
            ->setTableId('posts-table')
            ->columns($this->getColumns())
            ->minifiedAjax()
            ->orderBy(1)
            ->select(false)
            ->buttons([
                Button::make('create'),
                Button::make('export'),
                Button::make('print'),
                Button::make('reset'),
                Button::make('reload')
            ]);
    }

    public function getColumns(): array
    {
        $columns = [
            Column::computed('select_checkbox')
                ->title('<input type="checkbox" id="select-all-checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500">')
                ->exportable(false)
                ->printable(false)
                ->width(10)
                ->addClass('text-center')
                ->orderable(false)
                ->searchable(false)
                ->footer('')
                ->responsivePriority(1),
            Column::make('id')->responsivePriority(2),
            Column::make('title')->responsivePriority(3),
            Column::make('content')->responsivePriority(5),
            Column::make('published_at')
                ->title('Published At')
                ->width(150)
                ->responsivePriority(4),
            Column::make('is_published')
                ->title('Published')
                ->width(100)
                ->responsivePriority(4),
            Column::make('status')->responsivePriority(4),
        ];

        $columns[] = Column::computed('actions')
            ->title('ACTIONS')
            ->exportable(false)
            ->printable(false)
            ->width(120)
            ->addClass('text-center')
            ->orderable(false)
            ->searchable(false)
            ->footer('')
            ->responsivePriority(1);

        return $columns;
    }

    protected function filename(): string
    {
        return 'Posts_' . date('YmdHis');
    }
}


إنشاء المتحكم (Controller)

أنشئ فئة PostController واستبدل محتواها بالتالي:

php artisan make:controller PostController

سيكون مسار الملف المُنشأ: app/Http/Controllers/PostController.php. قم بتحديث محتواه كما يلي:

<?php

namespace App\Http\Controllers;

use App\DataTables\PostsDataTable;
use App\Models\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;

class PostController extends Controller
{
    public function index(PostsDataTable $postsDataTable)
    {
        $columns = $postsDataTable->getColumns();

        $formattedColumns = collect($columns)->map(function ($column) {
            return [
                'data' => $column->data,
                'name' => $column->name,
                'title' => $column->title,
                'orderable' => $column->orderable,
                'searchable' => $column->searchable,
                'className' => $column->className ?? '',
            ];
        })->toArray();

        return Inertia::render('Posts/Index', [
            'columns' => $formattedColumns,
        ]);
    }

    public function ajaxData(PostsDataTable $dataTable)
    {
        Log::info('معاملات طلب DataTables AJAX:', request()->all());
        return $dataTable->dataTable($dataTable->query(new Post()))->make(true);
    }

    public function create()
    {
        return view('tailwind-datatables::datatables.form', ['post' => new Post()]);
    }

    public function store(Request $request)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'nullable|string',
            'is_published' => 'nullable|boolean',
            'published_at' => 'nullable|date',
        ]);

        $data = $request->except(['published_at']);
        $data['published_at'] = $request->input('published_at') ? now()->parse($request->input('published_at')) : null;
        $data['is_published'] = (bool) $request->input('is_published', 0);

        $post = Post::create($data);

        return response()->json(['success' => 'تم إنشاء المنشور بنجاح.', 'post' => $post]);
    }

    public function edit(Post $post)
    {
        $post->published_at_formatted = $post->published_at ? $post->published_at->format('Y-m-d\TH:i') : '';

        return view('tailwind-datatables::datatables.form', compact('post'));
    }

    public function update(Request $request, Post $post)
    {
        $request->validate([
            'title' => 'required|string|max:255',
            'content' => 'nullable|string',
            'is_published' => 'boolean|nullable',
            'published_at' => 'nullable|date',
        ]);

        $data = $request->except(['published_at']);
        $data['is_published'] = (bool) $request->input('is_published', 0);
        $data['published_at'] = $request->input('published_at') ? now()->parse($request->input('published_at')) : null;

        $post->update($data);

        return response()->json(['success' => 'تم تحديث المنشور بنجاح.', 'post' => $post]);
    }

    public function destroy(Post $post)
    {
        try {
            $post->delete();
            return response()->json(['success' => 'تم حذف المنشور بنجاح.']);
        } catch (\Exception $e) {
            Log::error('حدث خطأ أثناء حذف المنشور: ' . $e->getMessage(), ['post_id' => $post->id]);
            return response()->json(['error' => 'حدث خطأ أثناء حذف المنشور.'], 500);
        }
    }
}



مكوّن Vue

أنشئ ملف resources/js/Pages/Posts/Index.vue واستبدل محتواه بالتالي:

mkdir -p resources/js/Pages/Posts
touch resources/js/Pages/Posts/Index.vue

سيكون مسار الملف المُنشأ: resources/js/Pages/Posts/Index.vue. قم بتحديث محتواه كما يلي:

<script setup lang="ts">
    import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';

    import { Head } from '[@inertiajs](https://github.com/inertiajs)/vue3';

    import { onMounted, ref } from 'vue';

    // تعريف خاصية columns القادمة من الخلفية

    const props = defineProps<{
        columns: Array<{
            data: string;

            name: string;

            title: string;

            orderable: boolean;

            searchable: boolean;

            className?: string; // خيار className اختياري
        }>;
    }>();

    console.log('Props Columns:', props.columns);

    // إنشاء ref لبنية HTML الخاصة بـ DataTables

    const dataTableContainer = ref<HTMLElement | null>(null);

    // تعريف إعداد crudDataTable باستخدام الأعمدة من props

    const datatableConfig = {
        datatableId: 'posts-table',

        ajaxUrl: route('posts.data'),

        columns: props.columns, // الحصول على الأعمدة من props!

        perPage: 10,

        perPageSelect: [10, 25, 50, 100, -1],

        addNewButtonText: 'إضافة منشور جديد',

        addEditUrl: route('posts.create'),

        updateUrlPrefix: route('posts.update', ''),

        deleteUrlPrefix: route('posts.destroy', ''),

        initialFormState: {},

        responsive: true, // تفعيل ميزة الاستجابة
    };

    // ref لتخزين نسخة DataTables

    const dataTableInstance = ref<any>(null);

    const $ = window.$ as any;

    onMounted(() => {
        // التأكد من توفر jQuery وDataTables عالمياً

        if (typeof window.$ === 'undefined' || typeof (window.$ as any).fn.DataTable === 'undefined') {
            console.error('jQuery أو DataTables غير متوفر عالمياً.');

            return;
        }

        // التحقق من وجود GorlabsDatatables

        if (typeof window.GorlabsDatatables === 'undefined') {
            console.error('GorlabsDatatables غير متوفر عالمياً.');

            return;
        }

        // تهيئة DataTables

        // نستخدم 'as any' لمنع TypeScript من تحميل نوع jQuery بشكل زائد

        dataTableInstance.value = $('#' + datatableConfig.datatableId).DataTable({...
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky