ENES TAŞ

Senior Software Developer

Asp.Net MVC ile FullCalendar Kullanımı -2 (Veritabanı İşlemleri) 24.6.2018

Web tabanlı uygulamalarda etkinlik yönetimi, randevu takibi gibi işlemler için kullanabileceğimiz fullcalendar eklentisiyle ilgili daha önce bir ön makale yazmıştım, ancak veritabanıyla birlikte örneklendirmek için vakit ayıramamıştım. 

Bu makalede Fullcalendar eklentisini veritabanına bağlı olarak örneklendirdim ve bu örnekte bootbox eklentisini de kullandım. Bootbox eklentisi web tabanlı uygulamalarınızda kullanabileceğiniz basit bir modal eklentisidir. http://bootboxjs.com/ adresinden inceleyebilirsiniz. 

Bootbox eklentisini kullanırken bootstrap kütüphanesine de ihtiyaç duyulduğu için ilk olarak projemize bootstrap kütüphanesini ekliyoruz. Solution üzerinden projemize sağ tıklayıp Manage Nuget Packages menüsünden bu işlemi yapabiliriz. 



Sonrasında bootbox eklentisini de Manage Nuget Packages menüsünden arayıp yükleyebilirsiniz. Bootstrap kütüphanesini ve bootbox eklentisini projemize ekledikten sonra Index sayfamıza css ve js dosyalarını referans olarak eklememiz gerekiyor. 

Alttaki resimde fosforlu sarıyla belirttim :)



Şimdi sıra geldi veritabanı tarafına :) Önce bir veritabanı oluşturuyoruz.

 


Ardından oluşturuduğumuz veritabanına bir tablo ekliyoruz. Siz ihtiyacınız doğrultusunda daha detaylı bir tablo oluşturabilirsiniz elbette. Ben basit bir örnek için böyle bir tablonun yeterli olacağını düşündüm.

Veritabanımız ve tablomuz hazır olduktan sonra proje kısmına tekrar dönüyoruz. Veritabanı bağlantısı ve işlemleri için basit bir class tanımladım. 

DBConnection.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web;

namespace FullCalendar.Models {
    public class DBConnection {

        private SqlConnection connection = null;
        private string connectionString = @"Data Source={SQLSUNUCUNUZ};Initial Catalog=FullCalendar;User ID={SQLKULLANICINIZ};Password={SQLPAROLANIZ};";

        /// <summary>
        /// Sql Bağlantısını açar
        /// </summary>
        public void OpenConnection() {
            connection = new SqlConnection(connectionString);

            if (connection.State != ConnectionState.Open)
                connection.Open();

        }
        /// <summary>
        /// Sql Bağlantısını kapatır
        /// </summary>
        public void CloseConnection() {
            if (connection != null && connection.State != ConnectionState.Closed) {
                connection.Close();
                connection.Dispose();
            }
        }

        /// <summary>
        /// Verilen sql cümlesiyle DataTable döndürür
        /// </summary>
        /// <param name="sql">Sql Cümlesi</param>
        /// <param name="param">Parametreler</param>
        /// <returns></returns>
        public DataTable GetDataTable(string sql, List<SqlParameter> param = null) {
            try {
                SqlCommand cmd = connection.CreateCommand();
                cmd.CommandText = sql;
                cmd.CommandType = CommandType.Text;
                if (param != null)
                    cmd.Parameters.AddRange(param.ToArray());
                SqlDataAdapter adapter = new SqlDataAdapter(cmd);
                DataTable dt = new DataTable();
                adapter.Fill(dt);
                cmd.Dispose();
                adapter.Dispose();
                return dt;
            }
            catch (Exception ex) {
                throw new Exception(ex.Message);
            }
        }
        /// <summary>
        /// Verilen sql cümlesini execute eder 
        /// </summary>
        /// <param name="sql">Sql Cümlesi</param>
        /// <param name="param">parametreler</param>
        public void RunSqlCommand(string sql, List<SqlParameter> param = null) {
            try {
                SqlCommand cmd = connection.CreateCommand();
                cmd.CommandText = sql;
                if (param != null)
                    cmd.Parameters.AddRange(param.ToArray());
                cmd.CommandType = CommandType.Text;
                cmd.ExecuteNonQuery();
                cmd.Dispose();
            }
            catch (Exception ex) {
                throw new Exception(ex.Message);
            }
        }
    }
}


Oluşturduğum class içindeki metodlarda gerekli dökümantasyonu sağladım, açıklayıcı olur umarım. Veritabanından tablo döndürebildiğimiz ve veritabanında insert,update,delete gibi sorguları çalıştırabildiğimiz iki metod bu örnek için işimizi görüyor. 


Önceki makalede CalendarController.cs adında bir controller nesnemiz vardı. Bu controller nesnemiz de 10 adet random olarak event nesnesi oluşturup fullcalendar takvim eklentisine yüklüyorduk. Şimdi bu işlemi veritabanına bağlı olarak yapacağız.

GetCalendarEvents metodumuzu aşağıdaki şekilde düzenledim. 

 /// <summary>
        /// Veritabanındaki kayıtları takvime doldurur
        /// </summary>
        /// <param name="start">Başlangıç tarihi parametresi</param>
        /// <param name="end">Bitiş tarihi parametresi</param>
        /// <returns></returns>
        public JsonResult GetCalendarEvents(string start, string end) {
            List<CalendarEvent> eventItems = new List<CalendarEvent>();

            DBConnection connect = new DBConnection();

            try {
                connect.OpenConnection();

                List<SqlParameter> param = new List<SqlParameter>();

                param.Add(new SqlParameter("@StartDate", start));
                param.Add(new SqlParameter("@EndDate", end));

                DataTable dt = connect.GetDataTable("Select * from tCalendar Where StartDate>=@StartDate and EndDate<=@EndDate", param);

                int i = 0, n = dt.Rows.Count;
                for (i = 0; i < n; i++) {
                    DataRow dr = dt.Rows[i];

                    CalendarEvent item = new CalendarEvent();
                    item.id = int.Parse(dr["IDCalendar"].ToString());
                    item.title = dr["Title"].ToString();
                    item.start = string.Format("{0:s}", dr["StartDate"]);
                    item.end = string.Format("{0:s}", dr["EndDate"]);
                    item.color = dr["Color"].ToString();
                    item.allDay = bool.Parse(dr["AllDay"].ToString());

                    eventItems.Add(item);
                }

                return Json(eventItems, JsonRequestBehavior.AllowGet);
            }
            finally {
                connect.CloseConnection();
            }
        }

Yaptığımız işlem veritabanına bağlanıp, verilen tarih aralığındaki kayıtları select sorgusuyla çekmek. Ve bu çektiğimiz verileri ilk makalede oluşturduğumuz CalendarEvent nesnesindeki ilgili alanlara set etmek. Sonrasında list olarak bu verileri view tarafına (Index.cshtml) response ediyoruz. 

FullCalendar eklentisinin events özelliğinde belirttiğimiz url kısmında bir değişiklik yapmamıza ihtiyaç yok. Önceki örnekte statik olarak liste oluşturup verileri takvime yüklemiştik, bu örnekte aynı işlemi veritabanımızdaki tablomuzdan çekerek sağladık. 

Veritabanına ekleme yapmak için CalendarController.cs sınıfımıza aşağıdaki metodu ekliyoruz. 

Veritabanına Ekleme İşlemi

   /// <summary>
        /// Veritabanına Ekleme yapar veya varolan kayıtta güncelleme yapar
        /// </summary>
        /// <param name="item">İşlem yapılması istenen event nesnesi</param>
        /// <returns></returns>
        public JsonResult AddOrEditItem(CalendarEvent item) {
            DBConnection connect = new DBConnection();
            try {
                connect.OpenConnection();
                List<SqlParameter> param = new List<SqlParameter>();

                param.Add(new SqlParameter("@Title", item.title));
                param.Add(new SqlParameter("@StartDate", item.start));
                param.Add(new SqlParameter("@EndDate", item.end));
                param.Add(new SqlParameter("@Color", item.color));
                param.Add(new SqlParameter("@AllDay", item.allDay));

                string sql = "Insert into tCalendar(Title,StartDate,EndDate,Color,AllDay) ";
                sql += "Values(@Title,@StartDate,@EndDate,@Color,@AllDay) ";

                connect.RunSqlCommand(sql, param);

                return Json(true, JsonRequestBehavior.AllowGet);
            }
            finally {
                connect.CloseConnection();
            }
        }


Javascript tarafında bu metodu tetiklemek için FullCalendar eklentsinin dayClick fonksiyonunu kullanacağız. dayClick fonksiyonu FullCalendar takvimi üzerinde herhangi bir güne tıklandığında tetiklenir.

Bu fonksiyon tetiklendiğinde çalışacak olan fonksiyon ise;

// Seçilen tarih için yeni event oluşturma, modal açar
        function NewItem(selectedDate) {
            var html = '';

            html += '<div class="row form-horizontal">';
            html += '<div class="col-md-12">';
            html += '<div class="form-group">';
            html += '<div class="col-lg-12 control-label">Başlık</div>';
            html += '<div class="col-lg-12">';
            html += '<input type="text" class="form-control" id="txtTitle" />';
            html += '</div>';
            html += '</div>';
            html += '</div>';
            html += '</div>';

            html += '<div class="row form-horizontal">';
            html += '<div class="col-md-12">';
            html += '<div class="form-group">';
            html += '<div class="col-lg-12 control-label">Başlangıç Tarihi</div>';
            html += '<div class="col-lg-12">';
            html += '<input type="text" class="form-control" id="txtStartDate" value="' + selectedDate + '" />';
            html += '</div>';
            html += '</div>';
            html += '</div>';
            html += '</div>';

            html += '<div class="row form-horizontal">';
            html += '<div class="col-md-12">';
            html += '<div class="form-group">';
            html += '<div class="col-lg-12 control-label">Bitiş Tarihi</div>';
            html += '<div class="col-lg-12">';
            html += '<input type="text" class="form-control" id="txtEndDate" value="' + selectedDate + '" />';
            html += '</div>';
            html += '</div>';
            html += '</div>';
            html += '</div>';

            html += '<div class="row form-horizontal">';
            html += '<div class="col-md-12">';
            html += '<div class="form-group">';
            html += '<div class="col-lg-12 control-label">Renk</div>';
            html += '<div class="col-lg-12">';
            html += '<select class="form-control" id="ddColor">';
            html += '<option value="red">Kırmızı</option>';
            html += '<option value="blue">Mavi</option>';
            html += '<option value="green">Yeşil</option>';
            html += '</select>';
            html += '</div>';
            html += '</div>';
            html += '</div>';
            html += '</div>';

            bootbox.dialog({
                message: html,
                title: "Yeni",
                size: "large",
                buttons: {
                    success: {
                        label: "Kaydet", className: "btn-success",
                        callback: function () {
                            var item = {
                                id: 0,
                                title: $('#txtTitle').val(),
                                color: $('#ddColor').val(),
                                start: $('#txtStartDate').val(),
                                end: $('#txtEndDate').val(),
                                allDay: true
                            }

                            SaveItem(item);
                        }
                    },
                    danger: {
                        label: "İptal",
                        className: "btn-default"
                    }
                }
            });
        }

Bu fonksiyon selectedDate adında bir parametre almakta, bu parametreyi takvimde herhangi bir güne tıkladığımızda yani dayClick fonksiyonunda göndereceğiz, tıklanan günün tarih bilgisini verecektir bize. Daha sonra basit bir html oluşturdum ve bootbox eklentisini kullanarak bu html dökümünü aşağıdaki gibi modalda gösterdim.

  Başlangıç ve bitiş tarihi alanları tıklamış olduğum tarih bilgisini içeriyor, başlık kısmını doldurup kaydete bastıktan sonra bu veri veritabanına yazılacak. Burada şu detayı atlamayalım, bootbox.dialog içerisinde buttons adında bir özellik var, kaydet ve iptal butonlarını burada ekliyoruz. Ancak kaydet butonunda bir function çalışması gerekiyor. onu da callback özelliği ile sağlıyoruz. callback fonksiyonunda modalda bulunan html elementlerinden bilgileri aldık ve item adında bir nesne oluşturduk. Bu nesneyi de SaveItem adında farklı bir fonksiyona gönderdik.

SaveItem fonksiyonu;

// modal'da girilen verileri kaydeder
        function SaveItem(item) {
            $.ajax({
                type: "POST",
                url: "/Calendar/AddOrEditItem/",
                data: "{\"item\":" + JSON.stringify(item) + "}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                async: true,
                error: function (request, status, error) {
                    var obj = jQuery.parseJSON(request.responseText);
                    bootbox.alert(obj.Message);
                },
                success: function (msg) {
                    $('#calendar').fullCalendar('refetchEvents');
                    $('#calendar').fullCalendar('unselect');
                }
            });
        }


SaveItem fonksiyonu item nesnemizi CalendarController.cs sınıfımızda bulunan AddOrEditItem isimli metoda parametre olarak post ediyor ve böylece girmiş olduğumuz bilgiler veritabanına yazılmış oluyor. ajax işleminde sonuç başarılı ise success kısmında fullCalendar takvimimizi yeniliyoruz. Sonuç resimde görüldüğü gibi başarılı :)



Sürükle bırak veya resize ile tarih bilgilerini güncelleme işlemi için ise Controller sınıfımıza aşağıdaki metodu ekleyelim

        /// <summary>
        /// Sürükle bırak işlemiyle tarih bilgilerini güncelleme
        /// </summary>
        /// <param name="id">ilgili kaydın id değeri</param>
        /// <param name="start">başlangıç tarihi değeri</param>
        /// <param name="end">bitiş tarihi değeri</param>
        /// <returns></returns>
        public JsonResult UpdateItemDate(int id, string start, string end) {
            DBConnection connect = new DBConnection();
            try {
                connect.OpenConnection();
                List<SqlParameter> param = new List<SqlParameter>();

                param.Add(new SqlParameter("@IDCalendar", id));
                param.Add(new SqlParameter("@StartDate", start));
                param.Add(new SqlParameter("@EndDate", end));

                string sql = "Update tCalendar Set StartDate=@StartDate, EndDate=@EndDate Where IDCalendar=@IDCalendar";

                connect.RunSqlCommand(sql, param);

                return Json(true, JsonRequestBehavior.AllowGet);
            }
            finally {
                connect.CloseConnection();
            }

        }


Bu metoda post işlemi sağlamak için fullCalendar eklentsinin eventDrop ve eventResize fonksiyonlarını kullanacağız. Bu fonksiyonların tetikleyeceği fonksiyon ise; 

        // sürükle bırak veya resize ile tarih bilgilerini güncelleme
        function UpdateItemDate(selectedItem) {
            debugger;

            var StartDate = selectedItem.start.format();
            var EndDate = selectedItem.end != null ? selectedItem.end.format() : selectedItem.start.format();

            $.ajax({
                type: "POST",
                url: "/Calendar/UpdateItemDate/",
                data: "{\"id\":" + selectedItem.id + ", \"start\":'" + StartDate + "', \"end\":'" + EndDate + "'}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                async: true,
                error: function (request, status, error) {
                    debugger;
                    var obj = jQuery.parseJSON(request.responseText);
                    bootbox.alert(obj.Message);
                },
                success: function (msg) {

                }
            });
        }

Bu fonksiyon takvim üzerinde sürüklediğimiz veya boyutlandırdığımız nesnenin id ve tarih bilgilerini CalendarController.cs de bulunan UpdateItemDate metodumuza post ediyor. 

Delete işlemi için ise Controller da bulunan metodumuz şu şekilde;

   /// <summary>
        /// Veritabanından kayıt siler
        /// </summary>
        /// <param name="id">Silinmesi istenilen kaydın id değeri</param>
        /// <returns></returns>
        public JsonResult DeleteItem(int id) {
            DBConnection connect = new DBConnection();
            try {
                connect.OpenConnection();
                List<SqlParameter> param = new List<SqlParameter>();

                param.Add(new SqlParameter("@IDCalendar", id));

                string sql = "Delete tCalendar Where IDCalendar=@IDCalendar";

                connect.RunSqlCommand(sql, param);

                return Json(true, JsonRequestBehavior.AllowGet);
            }
            finally {
                connect.CloseConnection();
            }

        }
Delete metodumuza da post işlemi sağlamak için ben fullCalendar eklentisinin eventClick fonksiyonunu kullandım. eventClick fonksiyonunun tetikleyeceği javascript fonksiyonumuz ise;

  // tıklanan event için silme onayı
        function DeleteItem(id) {
            bootbox.dialog({
                message: "Silmek istediğinize emin misiniz?",
                title: "Dikkat",
                buttons: {
                    success: {
                        label: "Evet", className: "btn-danger", callback: function () {
                            FnDelete(id);
                        }
                    },
                    danger: {
                        label: "Hayır",
                        className: "btn-default"
                    }
                }
            });
        }

        // tıklanan event'i siler
        function FnDelete(id) {
            jQuery.ajax({
                type: "POST",
                url: "/Calendar/DeleteItem/",
                data: "{'id':'" + id + "'}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                async: true,
                error: function (request, status, error) {
                    var obj = jQuery.parseJSON(request.responseText);
                    bootbox.alert(obj.Message);
                },
                success: function (msg) {
                    if (msg) {

                        $('#calendar').fullCalendar('refetchEvents');
                        $('#calendar').fullCalendar('unselect');

                        bootbox.alert("Silme işlemi başarılı");
                    }
                }
            });
        }

Burada öncelikle Delete fonksiyonu çalışıyor ve bootbox.dialog yardımıyla silme işlemi için onay istiyor.

 

Evet butonuna tıklandığında FnDelete fonksiyonu tetikleniyor ve DeleteItem metodumuza id bilgisi post ediliyor. Böylece veritabanından silme işlemini sağlamış oluyoruz. 

fullCalendar kısmı aşağıdaki gibi.

          $('#calendar').fullCalendar({
                lang: 'tr',
                header: {
                    left: 'prev,next today',
                    center: 'title',
                    right: 'month,agendaWeek,agendaDay'
                },
                editable: true,
                events: '/Calendar/GetCalendarEvents/',
                dayClick: function (date, jsEvent, view) {
                    NewItem(date.format());
                },
                eventClick: function (event) {
                    DeleteItem(event.id);
                },
                eventDrop: function (event, delta, revertFunc) {
                    UpdateItemDate(event);
                },
                eventResize: function (event, delta, revertFunc) {
                    UpdateItemDate(event);
                }
            });


Anlatım umarım faydalı olur, sanırım artık videolu anlatım hazırlamayı tercih edeceğim. Bu gibi örnekler makale olarak çok uzun oluyor. Projeyi de paylaştığım için makalenin daha anlaşılır olacağı kanaatindeyim. 

Projeyi buradan indirebilirsiniz. 

Kolay gelsin :)

Ceyhan - 05.10.2018 11:16

Gerçekten başarılı bir anlatım olmuş. İngilizce kaynaklar bu kadar anlaşılır değil. Emeğine sağlık.

Enes - 06.10.2018 13:14

Teşekkürler Ceyhan :)

Ahmet - 09.10.2018 01:33

Emeğine sağlık..

Enes - 10.10.2018 23:04

Teşekkürler :)

enes - 18.01.2019 16:36

Elinize sağlık gerçekten çok açıklayıcı olmuş

Enes - 18.01.2019 16:40

Teşekkürler :)

enes - 19.01.2019 17:24

Birşey sormak isityorum, veritbanından veriler geliyor ama calendar üzerinde görünmüyor. Ekrana veriyi gösteren kısım neresi

Enes - 19.01.2019 17:39

fullCalendar init işlemi sırasında events adında bir property var, orada veriyi çekeceğin url i belirtmen gerekiyor. events: '/Calendar/GetCalendarEvents/' gibi. proje örneği var aslında indirip inceleyebilirsin. çözemezsen enes@enestas.net mail adresime yazabilirsin, yardımcı olurum.

enes - 19.01.2019 20:31

Özelden de verdiğiniz destek için çok teşşekkürler. *Bir dip not düşeyim başkalarıda benimle aynı hataya düşmesin, - Calendar belli bir formatta kabul ediyor databaseden gelen verileri "GetCalendarEvents" bu fonksiyondaki for döngüsündeki koşullara mutlaka uymanız gerekiyor. - İkinci birşey de "AddOrEditItem" fonksiyonundaki id kullanıma dikkat edin, gelen id sadece kayıtlı mı değil mi diye kontrol ediyor, eğer sıfırdan kayıt yapıyorsanız ve KullanıcıID 'niz varsa ilk koşulda kendi ID nizi yazmanız gerekiyor. Diğer koşul zaten güncelleme işlemi olduğundan ID yi biliyor.

Enes - 19.01.2019 21:54

Dip not için teşekkürler :) ben aslında orada method ismini AddOrEditItem olarak vermiş olsam da sadece Add işlemini yaptırmışım, Edit işlemi sağlamamışım :) sadece sürükle bırak işlemlerinde tarih güncellemeleri için ayrı bir method yazmışım. Dediğin gibi aynı fonksiyonda id ile bir if kontrolü sağlanarak update veya insert işlemleri yaptırılabilir. Tabi update işlemi için calendar üzerinde event'e tıklandığında edit için bir popup form oluşturmak gerekiyor.

Tarık - 10.04.2019 00:03

Yaklaşık bir aydir, boş zaman buldukça multiselect calendar uygulamaya çalışıyorum daha bu kadar detaylı calendar ve veri tabanı ilişkisi anlatan makale görmedim. Öncelikle herşey için elinize sağlık, bende özelden iletişime geçip birseyler danışıp fikir öneri alabilir miyim.

Enes - 27.04.2019 02:43

Teşekkürler Tarık hocam, yorum yaptığınız tarihte askerde olduğum için yeni görüp dönüş yapabiliyorum :) elbette özelden iletişime geçebilirsiniz her zaman. iletişim sayfamda bilgilerim mevcut, oradaki hesaplardan bana ulaşabilirsiniz.

sadullah - 18.07.2019 11:44

Öncelikle paylaşımınız için Çok teşekkür ederim. Bu veri giriş ve güncelleme kısımları Ajandanın sağ kısmında yapmak istesek nasıl bir yol izlememiz gerekebilir. Bunun ile ilgili elinizde örnekler var mıdır acaba Şimdiden teşekkürler. İyi çalışmalar :)

Enes - 18.07.2019 12:30

Teşekkürler Sadullah bey :) Aslında teoride yine aynı işlemi yapacaksınız, veri girişini popup üzerinden yaptırmak yerine siz ajandanın sağında bir form tasarımı oluşturabilirsiniz. Ben bu örnekte ajandadaki bir güne tıklandığında NewItem fonksiyonu içerisinde form html çıktısı oluşturup popup açtırıyorum ve popup içerisinde gösteriyorum. siz html bölümünü statik olarak ajandanın sağında hazırlamış olacaksınız. dayClick fonksiyonunda sadece seçili tarihi set etme işlemi yapabilirsiniz . Anlatamadığım bir nokta varsa veya takıldığınız bir nokta olursa özelden iletişime geçebilirsiniz, yardımcı olmaya çalışırım :)

Yorum Yap