Merhabalar,


Bu yazıda Laravel ve Vue.js ile CRUD uygulaması yapımından bahsedeceğim. CRUD (Create-Read-Update-Delete) işlemi oluştur, oku, güncelle ve sil kelimelerinin İngilizce karşılıklarının baş harflerinden oluşan bir kelimedir. Örnek projede bir makale uygulaması yapacağım.


Örnek projeyi Github üzerinden paylaştım. İndirip inceleyebilirsiniz.


Örnek proje linki


İlk olarak komut satırında masaüstü dizinine gelerek aşağıdaki komutu yazıp yeni bir Laravel projesi oluşturuyorum:

composer create-project --prefer-dist laravel/laravel laravel-vuejs-crud-sample


Sonrasında Xampp yardımıyla yerelde bir veritabanı oluşturuyorum. Proje kök dizinindeki .env dosyasını açarak veritabanı bilgilerini aşağıdaki gibi düzenliyorum:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=article
DB_USERNAME=root
DB_PASSWORD=


defaultStringLength hatasıyla karşılaşmamak için app/Providers/AppServiceProvider.php dosyasını açarak boot() fonksiyonunu düzenliyorum:

public function boot()
{
   Schema::defaultStringLength(191);
}


Veritabanında makalelerin tutulacağı tabloyu oluşturmak adına bir migration tanımlıyorum:

php artisan make:migration create_articles_table

Ve migration dosyasının up() fonksiyonunu düzenliyorum:

public function up()
{
    Schema::create('articles', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('title');
        $table->text('body');
        $table->timestamps();
    });
}


Tanımladığım migration'ı çalıştırıyorum:

php artisan migrate


Veritabanındaki tabloyu temsil etmesi adına model dosyası oluşturuyorum:

php artisan make:model Article


api.php dosyasında gerekli route tanımlamalarını yapıyorum:

Route::get('articles', 'ArticleController@index');
Route::get('article/{id}', 'ArticleController@show');
Route::post('article', 'ArticleController@store');
Route::put('article', 'ArticleController@store');
Route::delete('article/{id}', 'ArticleController@destroy');


Controller dosyasını oluşturup gerekli fonksiyonları yazıyorum:

php artisan make:controller ArticleController

ArticleController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Article;

class ArticleController extends Controller
{
    public function index()
    {
        $articles = Article::orderBy('created_at', 'desc')->get()->toArray();
        return $articles;
    }

    public function store(Request $request)
    {
        $article = $request->isMethod('put') ? Article::findOrFail($request->article_id) : new Article;
        $article->id = $request->input('article_id');
        $article->title = $request->input('title');
        $article->body = $request->input('body');

        if ($article->save()) {
            return $article;
        }
    }

    public function show($id)
    {
        $article = Article::findOrFail($id);
        return $article;
    }

    public function destroy($id)
    {
        $article = Article::findOrFail($id);
        if ($article->delete()) {
            return $article;
        }
    }
}


Artık Vue.js kısmına geçebilirim:


Vue.js, Laravel projesi oluşturulduğunda gömülü olarak gelmektedir. Ancak Vue.js'i proje içerisinde kullanabilmek adına yapılması gereken ufak işlemler vardır. Şöyle ki;

  • package.json dosyasını açıp devDependencies kısmına aşağıdaki paketlerin eklendiğinden emin oluyorum (sizde sürümleri farklı olabilir);
"vue": "^2.5.17",
"vue-template-compiler": "^2.6.10"
  • Paketlerin yüklenmesi adına npm install komutunu çalıştırıyorum.


Vue.js kullanıma hazır hale geldikten sonra resources/js klasörünün altındaki components klasörüne gelip Navbar.vue ve Articles.vue adında iki adet dosya oluşturuyorum. Yine resources/js klasörünün altında bulunan app.js dosyasını açıp, oluşturmuş olduğum componentleri tanımlıyorum:

Vue.component('navbar', require('./components/Navbar.vue').default);
Vue.component('articles', require('./components/Articles.vue').default);


Buradaki şu kod satırına da dikkat ediyorum:

const app = new Vue({
    el: '#app',
});

Bu kod satırından anladığımız şu ki; Laravel projesi içerisinde app id li olan kısım (bu genellikle bir div etiketi olur) Vue.js kontrolünde olacak.


Şimdi de resources/views klasörünün altındaki welcome.blade.php dosyasını açıyorum ve gerekli düzenlemeleri yapıyorum:

<!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() }}">
        <script>window.Laravel = { csrfToken: '{{ csrf_token() }}' }</script>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">

        <title>Makale Uygulaması</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">        

        <style>
            .title {
                text-align: center;
            }
            .count {
                text-align: center; 
                font-size: 60px;
            }
            .article {
                margin-bottom: 20px;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <navbar></navbar>
            <div class="container">
                <articles></articles>
            </div>
        </div>

        <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
        <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
        <script src="{{ asset('js/app.js') }}"></script>
    </body>
</html>

Yapılan düzenlemeler;

  • Bootstrap, Jquery ve app.js dahil edildi.
  • app id li div tanımlandı.
  • app id li div içerisine Navbar ve Articles componentleri eklendi.


Navbar.vue component içeriği:

<template>
    <nav class="navbar navbar-expand-sm navbar-dark bg-info mb-2">
        <div class="container">
            <a href="/" class="navbar-brand">Makale Uygulaması</a>
        </div>
    </nav>
</template>


Articles.vue component içeriği:

<template>
  <div class="container">
    <div class="row">
      <div class="col-md-6">
          <h3 class="title">Makale Ekle</h3>
          <form @submit.prevent="addArticle" class="mb-3">
              <div class="form-group">
                  <input type="text" class="form-control" placeholder="Başlık" v-model="article.title"/>
              </div>
              <div class="form-group">
                  <textarea class="form-control" placeholder="Açıklama" v-model="article.body"></textarea>
              </div>
              <button type="submit" class="btn btn-primary btn-block">Kaydet</button>
          </form>
      </div>
      <div class="col-md-6">
        <h3 class="title">Toplam Makale</h3>
        <p class="count">{{ articles.length }}</p>
      </div>
    </div>
    <div class="row">
      <div class="col-md-4" v-for="article in articles" v-bind:key="article.id">
        <div class="card card-body article">
            <h4>{{ article.title }}</h4>
            <p>{{ article.body }}</p>
            <hr>
            <button @click="editArticle(article)" class="btn btn-warning mb-2">Düzenle</button>
            <button @click="deleteArticle(article.id)" class="btn btn-danger">Sil</button>
        </div>
      </div>          
    </div>
  </div>     
</template>

<script>
    export default {
      data() {
        return {
          articles: [],
          article: {
            id: '',
            title: '',
            body: ''
          },
          article_id: '',
          edit: false
        }
      },

      created() {
        this.fetchArticles();
      },

      methods: {
        fetchArticles() {
          fetch('/api/articles')
            .then(res => res.json())
            .then (res => {
                this.articles = res;
            })
            .catch(err => console.log(err));
        },
        addArticle() {
          if (this.edit === false) {
            fetch('api/article', {
              method: 'post',
              body: JSON.stringify(this.article),
              headers: {
                'content-type': 'application/json'
              }
            })
              .then(res => res.json())
              .then(data => {
                this.article.title = '';
                this.article.body = '';
                alert('Makale eklendi.');
                this.fetchArticles();
              })
          } else {
            fetch('api/article', {
              method: 'put',
              body: JSON.stringify(this.article),
              headers: {
                'content-type': 'application/json'
              }
            })
              .then(res => res.json())
              .then(data => {
                this.article.title = '';
                this.article.body = '';
                alert('Makale düzenlendi.');
                this.fetchArticles();
              })
          }
        },
        editArticle(article) {
          this.edit = true;
          this.article.id = article.id;
          this.article.article_id = article.id;
          this.article.title = article.title;
          this.article.body = article.body;
        },
        deleteArticle(id) {
          if (confirm('Makaleyi silmek istediğinize emin misiniz?')) {
            fetch(`api/article/${id}`, {
              method: 'delete'
            })
            .then(res => res.json())
            .then(data => {
                alert('Makale silindi.');
                this.fetchArticles();
            })
          }
        }        
      }
    }
</script>

Articles componenti içerisinde yapılan işlemleri adım adım açıklamak gerekirse;

  • script tag'ı içerisinde;
  • data() kısmında değişkenleri,
  • created() kısmında sayfa ilk yüklendiğinde yapılacak işlemleri,
  • methods kısmında fonksiyonları tanımlıyoruz.
  • Sayfa ilk yüklendiğinde fetchArticles fonksiyonunu çalıştırıyoruz. Bu fonksiyon makaleleri getiren api'yi (/api/articles) çağırarak gelen sonucu data kısmındaki articles dizisine eşitliyor.
  • Makale ekle kısmındaki Kaydet butonuna basıldığında formun @submit.prevent kısmında addArticle fonksiyonunu çağırıyoruz.
  • Makale ekle kısmındaki başlık ve açıklama input'larına v-model ile data kısmındaki article objemizi bağlıyoruz.
  • addArticle fonksiyonunda eğer edit değişkeni false ise başlık ve açıklamayı makale ekleme api'sine yollayarak makale ekleme işlemi yapıp, sonrasında input'ları sıfırlayıp, 'Makale eklendi.' uyarısı verip, makaleleri listeleyen fonksiyonu tekrar çağırıyoruz. edit değişkeni true ise başlık ve açıklamayı makale düzenleme api'sine yollayarak makale düzenleme işlemi yapıp, sonrasında input'ları sıfırlayıp, 'Makale düzenlendi.' uyarısı verip, makaleleri listeleyen fonksiyonu tekrar çağırıyoruz.
  • Toplam makale kısmına makale sayısını (articles.length) yazdırıyoruz.
  • v-for ile articles dizisini döngüye sokarak makaleleri listeliyoruz.
  • v-bind:key ile döngü içerisinde bulunan elemanların bir key'e (article.id) sahip olmasını sağlıyoruz.
  • Düzenle ve sil butonlarına @click event'ı vererek editArticle ve deleteArticle fonksiyonlarını çağırmalarını sağlıyoruz.
  • editArticle fonksiyonunda edit değişkenini true yapıp, data kısmındaki article objemizi tıklanan makalenin bilgileriyle dolduruyoruz.
  • deleteArticle fonksiyonunda 'Makaleyi silmek istediğinize emin misiniz?' uyarısı verip, onaylanması durumunda makale silme api'sini çağırıp tıklanan makaleyi siliyoruz. 'Makale silindi.' uyarısı verip, makaleleri listeleyen fonksiyonu tekrar çağırıyoruz.


Artık uygulamamızı test edebiliriz.


Vue.js üzerinde yapılan değişikliklerin işlemesi adına;

npm run watch

komutunu çalıştırıyoruz.


Laravel projesini çalıştırmak adına;

php artisan serve

komutunu çalıştırıyoruz.


Ve sonuç;


Umarım faydalı olmuştur.


İyi çalışmalar...