Laravel Ecommerce Api yazımı

Bu serimizde laravel api kullarak back-end yazalım.

https://github.com/abdullahsuhaisk/BasicEcommerceApiWithLaravel

Setting Up Enviroment

Eğer php server bilgisayarınızda yok ise xamp benzeri bir server kurulumu yapınız.

Composer kurulumunu yapınız.

Ardından composer ile global laravel kurulumunu yapınız.

Proje dizinine gelip

laravel new Eapi  komutu ile laravel projemizi oluşturalım

https://github.com/abdullahsuhaisk/BasicEcommerceApiWithLaravel

Projenin git Linki

CREATE API RESOURCE CONTROLLER


php artisan help make:model
Komutu ile yardım alabiliriz.

php artisan make:model Model/Product -a
Komutu ile Model klasörüme Product model dosyamı oluşturacağım. Aynı zamanda migrations ve resource controllerimi oluşturacak. Yani bana ne lazımsa tek bir komut ile oluşturuyorum.
 Projemde product, reviews modelleri olacak geniş ölçekliden ziyade best practise leri uyguylayacağımız mini bir proje olacak.

php artisan make:model Model/Review-a 
Komutu ile reviews lerimi oluşturuyorum.



Komutlarımı yazdıktan sonra Modellerim,Controllerim vs oluştu. Şimdi route oluşturuyoruz
Link alanına products girildiğinde productContoller imi gidecek.

php artisan route:list

Komutu ile route lerimi listeliyorum. Bir api için gerekli olan bütün methodlar tanımlandı(post,get vs)

Eğer create ve editmethodlarını iptal etmek istersem. Kodum şu şekilde olmalıydı;
Route::apiResource('/products','ProductController');

Kodumu bu şekilde değiştiriyorum. Değiştirdiğim için controller deki create ve edit methodlarını kaldırmam gerekiyor. Kaldırmasamda sorun olmaz.

|        | GET|HEAD  | api/products           | products.index    Bütün productlarımı görmem için
|        | POST      | api/products           | products.store     Yeni bir product ekleyebilmem için
|        | GET|HEAD  | api/products/{product} | products.show    Yalnızca bir product ı görebilmem için
|        | PUT|PATCH | api/products/{product} | products.update |
|        | DELETE    | api/products/{product} | products.destroy |

Reviews için route oluşturmam gerekiyor. Ama burada route parametremin şu şekilde olmasını istiyorum /products/11/reviews     

MİGRATİON

Migration php kodları vasıtası ile database de tablo oluşturmaya yarar.
Şimdi products migrations dosyamızı oluşturalım. Burada tablolarda tutmak için bize neler lazım diye düşünüyoruz.




$table->integer('product_id')->unsigned()->index();
$table->foreign('product_id')->references(id)->on('products')->onDelete('cascade');

Bu kod satırları ile şunları demek istiyorum.
review tablomda product id yi tutyorum. Çünkü her bir product ın reviewları olur ve bir productın birden fazla review ı olabilir.
İkinci satırda ise eğer bir product silinir ise ona ait olan bütün review ları sil demek istiyorum.

Şimdi sırada veritabanı bağlantıları yapmam var.
Xamp yada başka php server ımı açıyorum.

Yeni bir veritabanı oluşturuyorum.

.env


Ayarlarımıda yaptıktan sonra migrate komutu ile migrations larımı app ime entegre edeceğim.

php artisan migrate

Hata ile karşılaşırsanız çözüm

Tablolarımız oluştu.

DATABASE SEEDİNG WİTH FAKER LİBRARY

Bu bölümde faktory kullanarak fake datalar ile db mizi dolduracağız.



Buradaki PRODUCT->İD İLE product modelin içinden bir tane seçme işlemi yapıyoruz.
Sırada database/seeds klasörüne gelip run methodunda çalışacak methodları belirlemede.



review seed de hata yapmışız hataların düzeltilmiş hali

php artisan db:seed
Komutu ile seeding işleminin başlaması gerekiyor
Verilerim eklenmiş.

CREATİNG RELATİONSHİP
Bir product ın birden fazla reviews ı olur.
Bu ilişkinin anahtar kelimesi hasMany() dir.

Tam tersi bir tane review ın bir tane Product ı olabilir. Yani bir review bir ürün için yazılmıştır.
Bu ilişkinin anahtar kelimeside belongsTo() dur.


php artisan tinker
Komutu ile kontrol işlemi yapacağız.


 Gördüğünüz gibi komutlarım yardımıyla modelimde instance alabiliyorum. Ve model kodlarım sorunsuz çalışıyor.

CREATE APİ RESOURCE \ TRANSFORMER

php artisan make : resource Product/ProductCollection

Transformer' ın ne işe yaradığına değinmeden önce,
ProductControllerimize geri dönelim. index methodumun bütün productları dönecek şekilde düzenleyelim.
public function index()
{
return Product::all();
}
php artisan serve

Komutları ile app derleyelim. Önceki başlıklarda route oluşturmuştyk.

http://127.0.0.1:8000/api/products

giriş yaptığımızda karşımıza çıkan ekran ;

Sonuç olarak bütün productlarımın bütün bilgileri dönüyor. İşte transformers burada devreye girecek. Mesela ben api da id ve time bilgilerini vermek istemiyorum.

php artisan make : resource Product/ProductResource

Komutu ile bir tane daha resource oluşturacağım.


Artık db den gelen bilgiler yazılanlar şeklinde çıkacak. Şimdi ProductController ' e gidiyorum.

show methodunda productresource yi çağıracağım.




Transformer kullanarak çağırdım.

TRANSFORMİNG PRODUCTS

Artık product işlemi yaparken direk product modelimizi dönmüyoruz. Transformer olarak yazdığımız productResource yi kullanıyoruz. Burada review leri koymadık. Şimdi review ları koyalım. Ama link şeklinde yapalım. Projemizdeki route leri göreceğiz kod
php artisan route:list
Kullanacağım route buldum şimdi. ProductResource ma yazıyorum.




rating mantığını çözelim.
'raiting'=>$this->reviews()->sum('star'),
tüm starların toplamını gösteriyor.
'raiting'=>$this->reviews->sum('star')/$this->reviews->count(),
ortalamasını aldım.
Peki product ımızın hiç review ı yok ise ?
'raiting'=>$this->reviews->count() > 0 ? round($this->reviews->sum('star')/$this->reviews->count(),2): 'no raiting yet',
Çözümünü uyguladım.
Stok için ise
'stock'=>$this->stock == 0 ? 'Out of stock' : $this->stock,
benzer mantık uygulanır.
Şimdi total price yapalım.

ProductCollection nun son hali.

PRODUCTCOLLECTİON TRANSFORMİNG

Api products a istek yaptığımızda yine bütün productların bütün bilgileri geliyordu. Bu seferde bütün productların gelmesini fakat bazı bilgilerin gelmesini istiyorum.



ProductConroller de de index methodumu 
public function index(){    //return Product::all();    return ProductCollection::collection(Product::all());}
Yeni oluşturduğum resource sınıfından türetilmiş ProducCollection dönmesini sağlıyorum. 

CONFİGURE PASSPORT PACKPAGE
 Apı mıza bağlanırken kullanıcıların Authentice olup olmadıklarını kontorol etmek için kullanıyoruz.
composer require laravel/passport

komutu ile appi 'a passport kütüphanesini yüklüyoruz. Ardından
php artisan migrate
komutu ile passport ile gelen tabloları entegre migrate ediyoruz.
php artisan passport
komutu ile initilize işlemlerini gerçekleştiriyoruz. oauth_clients tablosuna iki yeni kayıt geldi. Client_ıd ve client_secret lerimiz burda.
Ardında user.php dosyamıza
class User extends Authenticatable{    use  HasApiTokens,Notifiable;
    protected $fillable = [        'name', 'email', 'password',    ];
    protected $hidden = [        'password', 'remember_token',    ];}

use  HasApiTokens
Satırını ekliyoruz. yani tokens kullanacağını user e belirttik.
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider{    protected $policies = [        'App\Model' => 'App\Policies\ModelPolicy',    ];    public function boot()    {        $this->registerPolicies();        Passport::routes();    }}
authService providers a eklemelerini yapalım.

Son olarak config/Api da
'guards' => [    'web' => [        'driver' => 'session',        'provider' => 'users',    ],
    'api' => [        'driver' => 'passport',        'provider' => 'users',    ],],
değişiklikleri yaptığımızda tamamlanmış olur.
Şimdi postMan 'e geçiş yapacağız. Ama öncesinde yeni kullanıcı oluşturmamız gerekiyor.
Daha önce hiç kullanıcı oluşturmamıştık.
php artisan make:auth
Komutu ile laravel in kendinden gelen Kullanıcı işlemlerini aktif edelim. ardından sitemizi local de çalıştırıp üye olalım.
Buradan kayıt oluyoruz.

Postman ile istek atacağız;


 CPost isreği attığımızda cevap olarak;

access_token geliyor  bu bizim giriş yaptığımızı haber veriyor.

Şimdi route 'ye tekrar bakalım :
Route::middleware('auth:api')->get('/user', function (Request $request) {    return $request->user();});
 api/user e get isteği yapalım. api routede kendiliğinden tanımlanmış bir routeydi bu. Üyenin giriş yapıp yapmadığını kullanan middleware kullanıyor.


Tekrar istek yaptığımızda üye bilgilerimizi veriyor.

CREATE NEW PRODUCT
Yeni bir ürün ekleneceği zaman ürün eklemek isteyen consumer 'in giriş olmasını istiyoruz.
php artisan route:list
Komutu ile route lerimi listeliyorum. Altını çizmiş olduğum işlerde autharization yani kullanıcının giriş yapmış olmasını beklemekteyim.

ProductController ıma gidiyorum ve contructor() yani kurucu methodumu oluşturuyorum. ProductControllerım ilk çalıştığında bütün methodlarımdan önce bu method çalışacak.

class ProductController extends Controller{
    public function __construct()    {        $this->middleware('auth:api')->except('index','show');        // ->except We don't need to index and show method with authenticate
    }
except ile index ve show methodlarımda middleware kullanmayacağımı belirtiyorum.

ProductControllerımın store methodunu şu şekilde güncelledim.
public function store(Request $request){    return 'Product ekleme işlemi';}

Şimdi postman aracılığı ile Auth olmadan post isteği atacağız ve authentica olarak post işlemi yapıp farkını göreceğiz.



Yeni bir request sınıfı oluşturalım
php artisan make:request ProductRequest 
komutu ile yeni bir request sınıfı oluşturuyorum.
app/http/request dizininin altında bulabileceğim bu oluşturduğum classın authorize() methodunu true yapacağım.Public function rules methodunada validate işlemlerini yapacağüım.

ProductRequest methodu:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ProductRequest extends FormRequest{    public function authorize()    {        return true;    }    public function rules()    {        return [            'name' => 'required|max:255|unique:products',            'description' => 'required',            'price' => 'required|max:10',            'stock' => 'required|max:6',            'discount' => 'required|max:2',        ];    }}
Şimdi controllere tekrar dönüş yapıp
public function store(ProductRequest $request){   }
Store methoduma parametre olarak request yerine ProductRequest gelmesini sağladım.
PostMan ile post işlemi yaptığımda tekrardan
The name field is required hatası alacam
Request yani gelen istekte aşağıdaki gibi alanlar dolu olmalıdır.


Peki yeni product kaydetme işlemini nasıl yapacağız ?
store methodumda Yeni bir product nesnesi oluşturacağız ve requestimden gelen attiributes leri oluşturduğum prodoct nesnesinin özelliklerine atayacaz.

Ekleme işlemi başarılı oldu.

PRODUCT GÜNCELLEME VE DETAY

Güncelleme işlemi yapabilmem için öncelikle  route nu bulmam gerekiyor.
php artisan route : list
komutu ile productlarıma bakabiliyorum.
api/products/{product}route adresinden güncelleme işlemlerimi yapacağım.
|        | PUT|PATCH | api/products/{product}                  | products.update                   | App\Http\Controllers\ProductController@update                             | api,auth:api |

153 ıd ye sahip productımı güncelleyeceğim.

ProductControllere gidiyorum.

public function update(Request $request, Product $product){    return $request->all();}
update methodumu güncelledim.


public function update(Request $request, Product $product){    return $product;}
Bu sefer gelen productımı döndürdüm sonuç product döndü. Şimdi controller methodumda parametre olarak gelen  product ı işleyip update yapacağım. 

public function update(Request $request, Product $product){    $product->update($request->all());    return $product;}
 Bunları yapmak için öncelikle product model ime gidip fillable dizisini doldurmam gerek.
Doludracağım değişkenler : name,details,stock,price,discount

protected $fillable = ['name','detail','stock','price','discount'];

 İşlemlerimi gerçekleştirdiğimde decription alanı güncellenmiyor.

Dikkat ederseniz fillable olarak description almadık. Onun yerine detail aldık. Yani yapmamız gerek işlemler var. Request den gelen dizinin içinde ki description yerine detail atayacağız. Ardından descriptionu sileceğiz.
public function update(Request $request, Product $product){    $request['detail'] = $request->description;    unset($request['description']);    $product->update($request->all());    return $product;}

Şimdi başarılı oldu ve gücellenmiş productımızı döndü.

PRODUCT SİLME

public function destroy(Product $product){    $product->delete();    return response(null,Response::HTTP_NO_CONTENT);}

Destroy methodum şu şekilde.  Reviews migrate dosyamdaki şu kod sayesinde product silindiğinde o producta ait reviewslerde siliniyor.

$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');          
Bu kod sayesinde.
Post man ile delete request gönderdiğimde silinme işlemi başarılı olur.

Yorumlar

Bu blogdaki popüler yayınlar

Laravel & React 1

React ile Kişisel Blog Sayfası Oluşturma 1

Github' a proje atma (Visual Studio Code ile)