Giriş yapmadınız.

Duyuru

TÜRKÇE KİTAP DUYURUSU:
Laravel 5 : Laravel 5.1 Güzelliği (Türkçe)
Vue.js 2 : Vue.js 2 Majesteleri (Türkçe)

#1 31.01.2019 00:01:59

kazim.karagul
Üye
Kayıtlı: 01.11.2014
Mesajlar: 175

Controller İçindeki Kodları API'ye Taşırken Hangi Yöntemi İzlerdiniz ?

Merhaba arkadaşlar,

Geliştirdiğim bir projede işlevsel kodları controller içine değil de api içine yazmaya karar verdim. Şimdi gerekli olmasa da müşterinin sözlerinden anlaşıldığı üzere ileride api gerekecek. Bu nedenle tüm kodları taşıyorum.
Örnek olması açısından aşağıda 2 modeli, 2 api içeriğini ve rotalarını inceleyebilirsiniz. Şimdilik takıldığım bir yer yok ama doğru yoldan gitmek adına sizin bu gibi bir durumda hangi işlemleri yaptığınızı merak ediyorum. 2 tane sorum var.

1) Gördüğünüz gibi rotalar 2 model için de çok sade olarak tanımlı. Siz olsanız Resource yapısını kullanmadan ilişkisel verileri çekmek için hangi yöntemi kullanırdınız? Ben bir kullanıcıya ait ilanları listelemek için /users/{user}/advertisements adında bir alt route olupturup User modeli içinde bir method oluşturmayı veya içeride yönlendirme yaparak /advertisements rotasına parametre ile veri göndermeyi düşünüyorum. Şimdilik karar vermedim.

2) Arayüzde kullanıcı bir ilan oluştururken veya yönetici panelde bir kullanıcı oluştururken formdan gelen bilgileri veritabanına kaydetmek için siz olsaydınız nasıl bir yol izlerdiniz ? Bu işlemleri eksiksiz yapan kodlar api tarafında olduğu için Frontend veya Backend tarafında bu işlemleri gerçekleştirmeden doğrudan api tarafına parametre ile bilgi mi gönderirdiniz yoksa aynı kodları kopyalayıp 2 tarafta da işlemleri gerçekleştirir miydiniz ?

Not: Api kodları bu kadar basit olarak kalmayacak. Sayfalama, form doğrulama, arama gibi işlemleri kod kalabalığı olmaması açısından eklemedim.

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    public function advertisements(){
        return $this->hasMany(App\Models\Advertisement::class);
    }
}
namespace App\Models;

class Advertisement extends Model
{
    public function user(){
        return $this->hasOne(App\Models\User::class);
    }
}
namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;

class ApiController extends Controller
{

}
namespace App\Http\Controllers\Api;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

class UserApi extends ApiController
{
    public function login(Request $request)
    {
        $user = User::where('email', $request->email)->first();
        if (Hash::check($request->password, $user->password)) {
            $user->api_token = str_random(60);
            $user->save();
            return response()->json([
                'status' => 200,
                'api_token' => $user->api_token,
                'username' => $user->name,
                'email' => $user->email,
                'id' => $user->id
            ]);
        }
        return response()->json([
            'status' => 401,
            'message' => 'Unauthenticated.'
        ]);
    }

    public function logout(Request $request)
    {
        $user = Auth::guard('api')->user();

        if ($user) {
            $user->api_token = null;
            $user->save();

            return response()->json([
                'status' => 401,
                'message' => 'User logged out.'
            ]);
        }else{
            return response()->json([
                'status' => 401,
                'message' => 'Unauthenticated.'
            ]);
        }
    }

    public function me()
    {
        $user = Auth::guard('api')->user();
        if (!$user) {
            return response()->json([
                'status' => 401,
                'message' => 'Unauthenticated.'
            ]);
        }
        return $user;
    }

    public function index()
    {
        return User::all();
    }

    public function show(User $article)
    {
        return $article;
    }

    public function store(Request $request)
    {
        $article = User::create($request->all());

        return response()->json($article, 201);
    }

    public function update(Request $request, User $article)
    {
        $article->update($request->all());

        return response()->json($article, 200);
    }

    public function delete(User $article)
    {
        try {
            $article->delete();
        } catch (\Exception $e) {
            return $e->getMessage();
        }
        return response()->json(null, 204);
    }
}
namespace App\Http\Controllers\Api;

use App\Models\Advertisement;
use Illuminate\Http\Request;

class AdvertisementApi extends ApiController
{
    public function index()
    {
        return Advertisement::all();
    }

    public function show(Advertisement $article)
    {
        return $article;
    }

    public function store(Request $request)
    {
        $article = Advertisement::create($request->all());

        return response()->json($article, 201);
    }

    public function update(Request $request, Advertisement $article)
    {
        $article->update($request->all());

        return response()->json($article, 200);
    }

    public function delete(Advertisement $article)
    {
        try {
            $article->delete();
        } catch (\Exception $e) {
            return $e->getMessage();
        }
        return response()->json(null, 204);
    }
}
Route::post('/login', 'Api\[email protected]');
Route::post('/logout', 'Api\[email protected]');

Route::group(['middleware' => 'auth:api'], function () {
    // User
    Route::get('users/me', 'Api\[email protected]');
    Route::get('users', 'Api\[email protected]');
    Route::get('users/{user}', 'Api\[email protected]');
    Route::post('users', 'Api\[email protected]');
    Route::put('users/{user}', 'Api\[email protected]');
    Route::delete('users/{user}', 'Api\[email protected]');

    // Advertisement
    Route::get('advertisements', 'Api\[email protected]');
    Route::get('advertisements/{advertisement}', 'Api\[email protected]');
    Route::post('advertisements', 'Api\[email protected]');
    Route::put('advertisements/{advertisement}', 'Api\[email protected]');
    Route::delete('advertisements/{advertisement}', 'Api\[email protected]');
});

Şimdiden teşekkürler.

Son düzenleyen kazim.karagul (31.01.2019 00:08:16)

Çevrimdışı

#2 31.01.2019 02:42:47

mgsmus
Moderatör
Yer: Mersin
Kayıtlı: 17.08.2013
Mesajlar: 1,496
Website

Yanıt: Controller İçindeki Kodları API'ye Taşırken Hangi Yöntemi İzlerdiniz ?

interface RepositoryContract {
    public function find($id);
    public function create(array $data);
}
class UserRepository implements RepositoryContract
{
    public function find($id)
    {
        return User::findOrFail($id);
    }

    public function create(array $data)
    {
        return User::create($data);
    }
}

class AdvertisementRepository implements RepositoryContract
{
    public function find($id)
    {
        return Advertisement::findOrFail($id);
    }

    public function create(array $data)
    {
        return Advertisement::create($data);
    }

    public function all()
    {
        return Advertisement::all();
    }
}

app/Providers/AppServiceProvider.php

public function register()
{
    // UserController sınıflarına RepositoryContract enjekte edildiğinde UserRepository sınıfını ver
    $this->app->when([UserController::class, Api\UserController::class])->needs(RepositoryContract::class)->give(UserRepository::class);

    // AdvertisementController sınıflarına RepositoryContract enjekte edildiğinde AdvertisementRepository sınıfını ver
    $this->app->when([AdvertisementController::class, Api\AdvertisementController::class])->needs(RepositoryContract::class)->give(AdvertisementRepository::class);
}

app/Http/Controllers/UserController.php

class UserController extends Controller
{
    protected $repository;

    // \App\Providers\AppServiceProvider::register() yöntemi içerisinde yaptığımız bağlama sayesinde
    // burada $repository olarak enjekte edilen sınıf otomatik olarak UserRepository oluyor.
    public function __construct(RepositoryContract $repository)
    {
        $this->repository = $repository;
    }

    public function show($id)
    {
        $user = $this->repository->find($id);

        return view('users.show', compact('user'));
    }
}

app/Http/Controllers/Api/UserController.php

class UserController extends Controller
{
    protected $repository;

    public function __construct(RepositoryContract $repository)
    {
        $this->repository = $repository;
    }

    public function show($id)
    {
        $user = $this->repository->find($id);

        return $user;
    }
}

app/Http/Controllers/AdvertisementController.php

class AdvertisementController extends Controller
{
    protected $repository;

    // \App\Providers\AppServiceProvider::register() yöntemi içerisinde yaptığımız bağlama sayesinde
    // burada $repository olarak enjekte edilen sınıf otomatik olarak AdvertisementRepository oluyor.
    public function __construct(RepositoryContract $repository)
    {
        $this->repository = $repository;
    }

    public function index()
    {
        $ads = $this->repository->all();

        return view('ads.index', compact('ads'));
    }
}

app/Http/Controllers/Api/AdvertisementController.php

class AdvertisementController extends Controller
{
    protected $repository;

    public function __construct(RepositoryContract $repository)
    {
        $this->repository = $repository;
    }

    public function index()
    {
        return $this->repository->all();
    }
}

Bu örneği yol göstermesi açısından kabaca hazırladım. Birçok şey eksik.

Kısaca anlatmak istediğim;

- Kolaylıkla değiştirilebilen hexagonal bir tasarım oluşturacaksınız ki kodun sadece belirli bölümünü söküp yerine yenisini taktığınızda sistem çalışmaya devam edecek.
- Yeni bir parça kolaylıkla eklenebilecek.

Laravel'in Service Container özelliği bu tür işler için size çok güzel yardımcı olacaktır:
https://laravel.com/docs/5.7/container

Ayrıca tasarım desenleri bu işin kalbi, sizi aynı kodları tekrar etmekten ve refaktör yapmaktan kurtaracaktır:
https://github.com/domnikl/DesignPatternsPHP

Rotalama ve API uçnoktalarının kullanımı için örnek teşkil etmesi açısından hoşuma giden DigitalOcean Api'sine bakabilirsiniz:
https://developers.digitalocean.com/documentation/v2/

Son olarak veri dönüşümlerini otomatikleştirmek ve standarta bağlamak için şu kütüphaneyi inceleyebilirsiniz:
https://fractal.thephpleague.com/

Çevrimdışı

#3 31.01.2019 15:00:14

deathisonitsway
Üye
Kayıtlı: 01.02.2016
Mesajlar: 394

Yanıt: Controller İçindeki Kodları API'ye Taşırken Hangi Yöntemi İzlerdiniz ?

Herşeyden önce @mgsmusun dediğini kullanabilmeniz icin.En cok ihtiyaç duyacağınız şey pattern mevzusudur.
Yoksa uğraşır durursunuz açıkcası.Coğu kişi controller içinde model kullanan insanlar(ız).Bu mevzu ustalık gerektirir maalesef,kodu ekleme çıkarmaya uygun halde aynı kararlılıkla çalıştıralabiliyor olması...

Destan yazılır bu konuda...


Laravel does not save you. Php knowledge only save you. Do not become a Laravel programmer, just the opposite,become php developer.

Çevrimdışı

#4 01.02.2019 01:07:51

kazim.karagul
Üye
Kayıtlı: 01.11.2014
Mesajlar: 175

Yanıt: Controller İçindeki Kodları API'ye Taşırken Hangi Yöntemi İzlerdiniz ?

Cevaplarınız için teşekkür ederim.
Projenin yeni versiyonu yeniden yazılacağı için şu anda geçici bir çözüm olarak en basit yolu seçmeye çalışıyorum. Yeniden yazılacaksa neden API yazma gereği duydun diyecek olursanız; yenisinin yazımı ileriki bir tarihte başlanacak ve daha büyük olacak. O zamana ladar api isteklerini karşılaşamak için böye bir girişimde bulunma gereği duydum.
Hani her yazılımcının tanıdığı o meşhur müşteri var ya; ne istediğini bilmeyen, her şeyi isteyen ama hiçbir şeyden memnun olmayan. İşte tam olarak öyle bir müşteri ile hakkında bilgimin olmadığı bir projeyi geliştirmeye çalışıyorum. Her an müşteri gelip "Süprizzz yeni modül çıktı hadi geliştirelim" diyor ve sizin de vurguladığınız kalitenin canına okuyor. Şu anda projenin durumunu görseniz beni siteden atarsınız tongue big_smile
Son süpriz yumurtadan çıkan işleri tamamladıktan sonra zamanın yettiği miktarca kodu tekrar temizleyeceğim.

Sadece Repository ile çalışarak ve mümkün olduğunca detayına inerek örnek proje yapan video linki biliyorsanız paylaşabilir misiniz? Kendi dokümanı ve diğer makaleler gayet iyi ama videolu anlatım olunca bazen daha farklı konulara da değinen eğitimciler oluyor.

Çevrimdışı

#5 01.02.2019 16:43:31

deathisonitsway
Üye
Kayıtlı: 01.02.2016
Mesajlar: 394

Yanıt: Controller İçindeki Kodları API'ye Taşırken Hangi Yöntemi İzlerdiniz ?

Sadece repository pattern değil,gerekirse diğer patternlarıda yeri gelince kullanırsınız.Bu şuna benzer,ben bu kodu daha iyi nasıl tasarlayabilirim ki ilerde değiştidiğimde hiç bir client etkilenmesin..Tüm mesele budur işte.Patternsiz büyük bir projeyi yazamazsınız..Yazarsanız hergününüz onunla cebelleşmekle geçer,hiç bir zaman stabil bir kod sağlayamazsınız.İşin daha kötüsü çalışsın artık deyip,koda bir ihanette siz yaparsınız.

Benim kullanıdıgım örnek bir patterni paylasayım...Tamamıyla hızlı bir senaryo ürünüm.Genişletip değiştirebilirsin.
Aşağıda ana bir fabrika girişi bulunmaktadır...Bu fabrika girişi fabrikada üretilecek ürünleri servis eden ürün yöneticisi görevinde bir distrübütore sahip..


class Factory extends FactoryManager
{
    /**
     * @return ProductsInterface
     */
    public static function products() : ProductsInterface
    {
        return static::singleton(ProductsManager::class);
    }
}

Fabrikanın olmazsa olmaz üretilecek hizmetlerin sözleşmelerini imzalamak zorundayız.
ve bu fabrika da ürün yöneticisi çikolata ve kek ürünlerini üretsin...


interface ProductsInterface
{
    /**
     * @return ChocolatesInterface
     */
    public function chocolates();
    
    /**
     * @return CakesInterface
     */
    public function cakes();
    
}

Fabrika sınıfını genişleten bir resource yaparsınız.Burada products fabrikasına bağlı tüm ürünleri methodsal olarak yazıp her bir methodun gerçek işi yapacağı ürün objelerini yazarsınız.

class ProductsManager extends FactoryManager implements ProductsInterface
{
    /**
     * @return ChocolatesInterface
     */
    public function chocolates() : ChocolatesInterface
    {
        return new Chocolates($this->factory);
    }
    
    /**
     * @return CakesInterface
     */
    public function cakes() : CakesInterface
    {
        return new Cakes($this->factory);
    }
}

Products fabrikasından çıkan cakes objem aşağıdaki gibi.Cakes objesi üzümlü kek yapsın smile coğaltabilirsiniz...

class Cakes extends FactoryManager implements CakesInterface
{
     public function withGrape()
    {
        return 'withGrape';
    }
}

Products fabrikasından çıkan chicolates objem aşağıdaki gibi.chicolates objesi sütlü çikolata yapsın smile coğaltabilirsiniz...

class Chocolates extends FactoryManager implements ChocolatesInterface
{
    public function withMilk()
    {
        return 'withMilk';
    }
}

en nihayetinde ana factory managerinize bağlanan bir helper yaptığınızı varsayarak smile controllercü icinde şöyle ulaşırsınız.


factory()->products->chocolates->withMilk();

factory()->products->cakes->withGrape();

blabla foo işte..:) Güzel bir ide kullandığınızda bu annotationlarla kod kendini kendini yazar siz yazmazsınız..yazdığınız koddan keyif alır ve onu cok rahat yonetebilirsiniz.Cunku herkes kendi işini yapacak.

Son düzenleyen deathisonitsway (01.02.2019 16:48:09)


Laravel does not save you. Php knowledge only save you. Do not become a Laravel programmer, just the opposite,become php developer.

Çevrimdışı

Forum alt kısmı