14. Uygulama alıştırması – sürüm 6

Az önce aşağıdaki katmanlı yapıyı uyguladık:

Örneklerde kullanılan SGBD, MySQL idi. Bağlantı başlığında, [dao] katmanını uygulayan sınıfta, belirli bir SGBD kullanıldığını düşündürecek hiçbir şeyin bulunmadığını belirtmiştik. Şimdi bunu, başka bir SGBD olan SGBD ve PostgreSQL’i kullanarak doğrulayacağız. Katmanlı mimari şu şekilde olur:

14.1. SGBD ve PostgreSQL'in kurulumu
SGBD ve PostgreSQL sürümleri, URL ve [https://www.postgresql.org/download/] (Mayıs 2019) sürümlerinde mevcuttur. Burada 64 bit Windows sürümünün kurulumunu gösteriyoruz:


- [1-4] adresinden, SGBD kurulum dosyasını indirin;
İndirilen yükleyiciyi çalıştırın:

- [6]'te bir kurulum klasörü belirtin;

- [8] adımında, [Stack Builder] seçeneği burada yapmak istediğimiz şey için gereksizdir;
- [10]'te, size gösterilen değeri olduğu gibi bırakın;

- [12-13]'te, buraya [root] şifresini girdik. Bu, SGBD adlı [postgres]'in yönetici şifresi olacaktır. PostgreSQL'e aynı zamanda süper kullanıcı da denir;
- [15]'te varsayılan değeri bırakın: bu, SGBD'in dinleme portudur;

- [17]'te varsayılan değeri değiştirmeyin;
- [19]'te, kurulum yapılandırmasının özeti;


Windows'ta, SGBD ve PostgreSQL, otomatik olarak başlatılan bir Windows hizmeti olarak yüklenir. Çoğu zaman bu durum istenmez. Bu yapılandırmayı değiştireceğiz. Windows arama çubuğuna [services] yazın:

- [29] olarak değiştirin; SGBD ve PostgreSQL hizmetlerinin otomatik modda olduğunu görebilirsiniz. [30] hizmetinin özelliklerine erişerek bunu değiştirelim:

- [31-32]'te, başlatma modunu manuel olarak ayarlayın;
- [33]'te hizmeti durdurun;
SGBD hizmetini manuel olarak başlatmak istediğinizde, [services] uygulamasına geri dönün, [postgresql] hizmetine sağ tıklayın (34) ve hizmeti başlatın (35).
14.2. SGBD ve PostgreSQL için PDO uzantısının etkinleştirilmesi
PHP'i yapılandıran [php.ini] dosyasını değiştireceğiz (bkz. bağlantı paragrafı):

- dosyasını [2] olarak değiştirin ve PostgreSQL dosyasının PDO uzantısının etkinleştirildiğini kontrol edin. Bunu yaptıktan sonra, değişikliği kaydedin ve değişikliğin uygulanacağından emin olmak için Laragon'u yeniden başlatın. Ardından, PHP yapılandırmasını doğrudan Laragon [3-5] üzerinden kontrol edin.
14.3. PostgreSQL'i [pgAdmin] aracıyla yönetme
SGBD PostgreSQL Windows hizmetini başlatın (bkz. bağlantı paragrafı). Ardından, [services] aracını başlattığınız şekilde, [pgadmin] aracını başlatın; bu araç, SGBD, PostgreSQL ve [1-3]'i yönetmenizi sağlar:

Belirli bir anda sizden süper kullanıcı şifresi istenebilir. Bu şifre [postgres]'tir. Bu şifreyi, SGBD kurulumunda belirlemiştiniz. Bu belgede, kurulum sırasında süper kullanıcıya [root] şifresini vermiştik.
- [4]'te, [pgAdmin] bir web uygulamasıdır;
- [5]'te, [pgAdmin] tarafından algılanan PostgreSQL sunucularının listesi, burada 1;
- [6]'te, bizim başlattığımız PostgreSQL sunucusu;
- [7]'te, SGBD'in veritabanları, burada 1;
- [8]'te, [postgresql] veritabanı, [postgres] süper kullanıcısı tarafından yönetilir;
Öncelikle [admimpots] adlı bir kullanıcı ve [mdpimpots] şifresini oluşturalım:


- [17]'te [mdpimpots] yazdık;

- [21]'te, [pgAdmin] aracının SGBD ve PostgreSQL'e göndereceği SQL kodu. Bu, PostgreSQL'e ait SQL dilini öğrenmenin bir yoludur;
- [22]'te, [Save] sihirbazının onaylanmasının ardından, [admimpots] kullanıcısı oluşturuldu;
Şimdi [dbimpots-2019] veritabanını oluşturuyoruz:

[23]'e sağ tıklayıp ardından [24-25]'e tıklayarak yeni bir veritabanı oluşturuyoruz. [26] sekmesinde, [27] veritabanının adını ve [admimpots] [28] veritabanının sahibini tanımlıyoruz.

- [30]'te, veritabanının oluşturulma kodu SQL;
- [31]'e, [Save] sihirbazının onaylanmasından sonra [dbimpots-2019] veritabanı oluşturulur;
Şimdi, [id, limites, coeffr, coeffn] sütunlarını içeren [tbtranches] tablosunu oluşturacağız. PostgreSQL'in bir özelliği, sütun adlarının büyük/küçük harfe duyarlı olmasıdır; bu, diğer SGBD'lerde genellikle geçerli değildir. Dolayısıyla, MySQL dosyasında, [select limites, coeffR, coeffN from tbtranches] komutu, [tbtranches] tablosundaki gerçek sütunlar [LIMITES, COEFFR, COEFFN] olsa bile çalışacaktır. PostgreSQL durumunda ise SQL komutu çalışmayacaktır. Bu durumda [select LIMITES, COEFFR, COEFFN from tbtranches] yazılabilir, ancak bu da yine çalışmayacaktır; çünkü PostgreSQL, [select limites, coeffr, coeffn from tbtranches] komutunu çalıştıracaktır: varsayılan olarak sütun adlarını küçük harflerle geçirir. Bunun yapılmaması için şunu yazmak gerekir: [select "LIMITES", "COEFFR", "COEFFN" from tbtranches], yani sütun adlarını tırnak işaretleriyle korumak gerekir. Bu nedenlerle, sütunlara küçük harfli adlar vereceğiz. Veritabanındaki nesnelerin adları, SGBD ile uyumsuzluklara neden olabilir; çünkü bazı adlar, belirli SGBD'lerde ayrılmış kelimeler iken, diğerlerinde değildir.
[tbtranches] tablosunu oluşturuyoruz:

- sütun oluşturmak için [40] düğmesini kullanın;


- [Save] ile oluşturma sihirbazını tamamladıktan sonra, [tbtranches] tablosu oluşturulur [52-53];
SGBD'e, tabloya bir satır eklendiğinde [id] birincil anahtarını kendisinin oluşturması gerektiğini belirtmeliyiz:

- [56]'te [id] birincil anahtarının özelliklerine erişilir;
- [59]'te, sütunun türünün [Identity] olduğu belirtilir. Bu, SGBD'in birincil anahtar değerlerini oluşturmasına neden olur;

- [62]'te, bu işlem için oluşturulan SQL kodu;
[tbtranches] tablosu artık hazırdır.
Aynı işlemleri tekrarlayarak [tbconstantes] tablosunu oluşturuyoruz. Elde edilecek sonucu veriyoruz:



[dbimpots-2019] veritabanı artık hazırdır. Bu veritabanını verilerle dolduracağız.
MySQL'te yaptığımız gibi, [dbimpots-2019] veritabanını bir SQL dosyasına aktarabiliriz. Daha sonra, veritabanını kaybettiyseniz veya veritabanı bozulduysa, bu SQL dosyasını içe aktararak veritabanını yeniden oluşturabilirsiniz. Burada yalnızca veritabanının yapısını dışa aktaracağız, verilerini değil:


Oluşturulan dosya şöyledir:
--
-- PostgreSQL veritabanı dökümü
--
-- Veritabanı sürümü 11.2'den döküm alınmıştır
-- pg_dump sürüm 11.2 ile dökümlenmiştir
-- 4 Temmuz 2019 saat 08:20:31'de başlatıldı
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_with_oids = false;
--
-- TOC girişi 198 (sınıf 1259 OID 16408)
-- Ad: tbconstantes; Tür: TABLE; Şema: public; Sahibi: postgres
--
CREATE TABLE public.tbconstantes (
plafond_qf_demi_part double precision NOT NULL,
id integer NOT NULL,
plafond_revenus_celibataire_pour_reduction double precision NOT NULL,
plafond_revenus_couple_pour_reduction double precision NOT NULL,
valeur_reduc_demi_part double precision NOT NULL,
plafond_decote_celibataire double precision NOT NULL,
plafond_decote_couple double precision NOT NULL,
plafond_impot_celibataire_pour_decote double precision NOT NULL,
plafond_impot_couple_pour_decote double precision NOT NULL,
abattement_dix_pourcent_max double precision NOT NULL,
abattement_dix_pourcent_min double precision NOT NULL
);
ALTER TABLE public.tbconstantes OWNER TO postgres;
--
-- TOC girişi 199 (sınıf 1259 OID 16411)
-- Ad: tbconstantes_id_seq; Tür: SEQUENCE; Şema: public; Sahibi: postgres
--
ALTER TABLE public.tbconstantes ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY (
SEQUENCE NAME public.tbconstantes_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1
);
--
-- TOC girişi 196 (sınıf 1259 OID 16399)
-- Ad: tbtranches; Tür: TABLE; Şema: public; Sahibi: admimpots
--
CREATE TABLE public.tbtranches (
limites double precision NOT NULL,
id integer NOT NULL,
coeffr double precision NOT NULL,
coeffn double precision NOT NULL
);
ALTER TABLE public.tbtranches OWNER TO admimpots;
--
-- TOC girişi 197 (sınıf 1259 OID 16404)
-- Ad: tbimpots_id_seq; Tür: SEQUENCE; Şema: public; Sahibi: admimpots
--
ALTER TABLE public.tbtranches ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY (
SEQUENCE NAME public.tbimpots_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1
);
--
-- TOC girişi 2694 (sınıf 2606 OID 16429)
-- Ad: tbconstantes tbconstantes_pkey; Tür: CONSTRAINT; Şema: public; Sahibi: postgres
--
ALTER TABLE ONLY public.tbconstantes
ADD CONSTRAINT tbconstantes_pkey PRIMARY KEY (id);
--
-- TOC girişi 2692 (sınıf 2606 OID 16403)
-- Ad: tbtranches tbimpots_pkey; Tür: CONSTRAINT; Şema: public; Sahibi: admimpots
--
ALTER TABLE ONLY public.tbtranches
ADD CONSTRAINT tbimpots_pkey PRIMARY KEY (id);
--
-- TOC girişi 2821 (sınıf 0 OID 0)
-- Bağımlılıklar: 198
-- Ad: TABLE tbconstantes; Tür: ACL; Şema: public; Sahibi: postgres
--
GRANT ALL ON TABLE public.tbconstantes TO admimpots;
-- 4 Temmuz 2019 08:20:32 tarihinde tamamlandı
--
-- PostgreSQL veritabanı dökümü tamamlandı
--
14.4. [tbtranches] tablosunun doldurulması
Bu işlemi, bağlantı paragrafında yer alan SGBD ve MySQL dosyalarıyla daha önce gerçekleştirmiştik. Şimdi, veritabanını tanımlayan [database.json] dosyasını değiştirmemiz yeterli:

[database.json] dosyası şu şekilde olur:
{
"dsn": "pgsql:host=localhost;dbname=dbimpots-2019",
"id": "admimpots",
"pwd": "mdpimpots",
"tableTranches": "public.tbtranches",
"colLimites": "limites",
"colCoeffR": "coeffr",
"colCoeffN": "coeffn",
"tableConstantes": "public.tbconstantes",
"colPlafondQfDemiPart": "plafond_qf_demi_part",
"colPlafondRevenusCelibatairePourReduction": "plafond_revenus_celibataire_pour_reduction",
"colPlafondRevenusCouplePourReduction": "plafond_revenus_couple_pour_reduction",
"colValeurReducDemiPart": "valeur_reduc_demi_part",
"colPlafondDecoteCelibataire": "plafond_decote_celibataire",
"colPlafondDecoteCouple": "plafond_decote_couple",
"colPlafondImpotCelibatairePourDecote": "plafond_impot_celibataire_pour_decote",
"colPlafondImpotCouplePourDecote": "plafond_impot_couple_pour_decote",
"colAbattementDixPourcentMax": "abattement_dix_pourcent_max",
"colAbattementDixPourcentMin": "abattement_dix_pourcent_min"
}
- 2. satır: DSN değişti; [pgsql], SGBD Postgres ile uğraştığımızı gösterir;
- 5. ve 9. satırlar: Tablo adlarının önüne, ait oldukları şema adı olan [public] eklenmiştir. Tablo adında herhangi bir şema belirtilmediğinde varsayılan olarak [public] şeması kullanıldığından, bu değişiklik zorunlu değildi;
- 6-8. ve 10-19. satırlar: sütun adları değiştirilmiştir;
[dbimpots-2019] veritabanını doldurmaya yarayan [MainTransferAdminDataFromJsonFile2PostgresDatabase.php] komut dosyası şu şekildedir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı bir şekilde uyuldu
declare (strict_types=1);
// ad alanı
namespace Application;
// PHP ile hata yönetimi
// ini_set("display_errors", "0");
// arayüz ve sınıfların dahil edilmesi
require_once __DIR__ . "/../../version-05/Entities/BaseEntity.php";
require_once __DIR__ . "/../../version-05/Entities/TaxAdminData.php";
require_once __DIR__ . "/../../version-05/Entities/TaxPayerData.php";
require_once __DIR__ . "/../../version-05/Entities/Database.php";
require_once __DIR__ . "/../../version-05/Entities/ExceptionImpots.php";
require_once __DIR__ . "/../../version-05/Utilities/Utilitaires.php";
require_once __DIR__ . "/../../version-05/Dao/InterfaceDao.php";
require_once __DIR__ . "/../../version-05/Dao/TraitDao.php";
require_once __DIR__ . "/../../version-05/Dao/InterfaceDao4TransferAdminData2Database.php";
require_once __DIR__ . "/../../version-05/Dao/DaoTransferAdminDataFromJsonFile2Database.php";
//
// sabitlerin tanımlanması
const DATABASE_CONFIG_FILENAME = "../Data/database.json";
const TAXADMINDATA_FILENAME = "../Data/taxadmindata.json";
//
try {
// katman oluşturma [dao]
$dao = new DaoTransferAdminDataFromJsonFile2Database(DATABASE_CONFIG_FILENAME, TAXADMINDATA_FILENAME);
// verilerin veritabanına aktarılması
$dao->transferAdminData2Database();
} catch (ExceptionImpots $ex) {
// hatanın görüntülenmesi
print "L'erreur suivante s'est produite : " . utf8_encode($ex->getMessage()) . "\n";
}
// son
print "Terminé\n";
exit;
Açıklamalar
Yalnızca uygulamanın çalışması için gerekli dosyaları yükleyen 12-21. satırlar değişir. Bunlar, [__DIR__] değeri değiştiği için değişir: bu değer artık [version-07/Main] klasörünü işaret etmektedir.
Bu komut dosyası çalıştırıldığında, [tbtranches] tablosunda şu sonuç elde edilir:

- [1] üzerine sağ tıklayıp ardından [2-3]'e tıklayın;
- [4]'te vergi dilimlerine ait veriler doğru şekilde görünür;
Aynı işlemi [tbconstantes] sabit tablosu için de tekrarlayın:



Komut dosyasının çalıştırılması için Laragon uygulamasının aktif olması gerekmediğine dikkat edilmelidir: Ne Apache sunucusuna ne de SGBD ve MySQL'e ihtiyaç vardır. Yalnızca Windows hizmetini başlattığımız SGBD PostgreSQL'e ihtiyaç vardır.
14.5. Vergi hesaplaması

[dao] (3) ve [métier] (2) katmanları zaten yazılmıştır. SGBD ve MySQL için ana komut dosyasını “bağlantı” başlıklı paragrafta daha önce yazmıştık. Şimdi tek yapmamız gereken, [MainCalculateImpotsWithTaxAdminDataInMySQLDatabase.php] betiğini alıp SGBD ve PostgreSQL için uyarlamak. Artık adı [MainCalculateImpotsWithTaxAdminDataInPostgresDatabase.php]:

[MainCalculateImpotsWithTaxAdminDataInPostgresDatabase.php] komut dosyası şu şekildedir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı sıkıya uyulması
declare (strict_types=1);
// ad alanı
namespace Application;
// PHP ile hata yönetimi
//ini_set("display_errors", "0");
// arayüz ve sınıfların dahil edilmesi
require_once __DIR__ . "/../../version-05/Entities/BaseEntity.php";
require_once __DIR__ . "/../../version-05/Entities/TaxAdminData.php";
require_once __DIR__ . "/../../version-05/Entities/TaxPayerData.php";
require_once __DIR__ . "/../../version-05/Entities/Database.php";
require_once __DIR__ . "/../../version-05/Entities/ExceptionImpots.php";
require_once __DIR__ . "/../../version-05/Utilities/Utilitaires.php";
require_once __DIR__ . "/../../version-05/Dao/InterfaceDao.php";
require_once __DIR__ . "/../../version-05/Dao/TraitDao.php";
require_once __DIR__ . "/../../version-05/Dao/DaoImpotsWithTaxAdminDataInDatabase.php";
require_once __DIR__ . "/../../version-05/Métier/InterfaceMetier.php";
require_once __DIR__ . "/../../version-05/Métier/Metier.php";
//
// sabitlerin tanımlanması
const DATABASE_CONFIG_FILENAME = "../Data/database.json";
const TAXADMINDATA_FILENAME = "../Data/taxadmindata.json";
const RESULTS_FILENAME = "../Data/resultats.json";
const ERRORS_FILENAME = "../Data/errors.json";
const TAXPAYERSDATA_FILENAME = "../Data/taxpayersdata.json";
try {
// [dao] katmanının oluşturulması
$dao = new DaoImpotsWithTaxAdminDataInDatabase(DATABASE_CONFIG_FILENAME);
// katman oluşturma [métier]
$métier = new Metier($dao);
// toplu iş modunda vergi hesaplaması
$métier->executeBatchImpots(TAXPAYERSDATA_FILENAME, RESULTS_FILENAME, ERRORS_FILENAME);
} catch (ExceptionImpots $ex) {
// hatanın görüntülenmesi
print "Une erreur s'est produite : " . utf8_encode($ex->getMessage()) . "\n";
}
// son
print "Terminé\n";
exit;
Açıklamalar
Yalnızca uygulamanın çalışması için gerekli dosyaları yükleyen 12-22. satırlar değişiyor. Bu satırlar, [__DIR__] değeri değiştiği için değişiyor: bu değer artık [version-07/Main] klasörünü işaret ediyor.
Çalıştırma Sonuçları
Önceki sürümlerde elde edilenlerle aynıdır.
14.6. [Codeception] Testleri
Önceki sürümlerde olduğu gibi, bu sürümü de [Codeception] testleriyle doğruluyoruz:

14.6.1. [dao] Katman Testi
[DaoTest.php] testi şu şekildedir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı bir şekilde uyulur
declare (strict_types=1);
// ad alanı
namespace Application;
// kök dizinler
define("ROOT", "C:/Data/st-2019/dev/php7/poly/scripts-console/impots/version-06");
define("VENDOR", "C:/myprograms/laragon-lite/www/vendor");
// arayüz ve sınıfların dahil edilmesi
require_once ROOT . "/../version-05/Entities/BaseEntity.php";
require_once ROOT . "/../version-05/Entities/TaxAdminData.php";
require_once ROOT . "/../version-05/Entities/TaxPayerData.php";
require_once ROOT . "/../version-05/Entities/Database.php";
require_once ROOT . "/../version-05/Entities/ExceptionImpots.php";
require_once ROOT . "/../version-05/Utilities/Utilitaires.php";
require_once ROOT . "/../version-05/Dao/InterfaceDao.php";
require_once ROOT . "/../version-05/Dao/TraitDao.php";
require_once ROOT . "/../version-05/Dao/DaoImpotsWithTaxAdminDataInDatabase.php";
// üçüncü taraf kütüphaneler
require_once VENDOR . "/autoload.php";
// sabitlerin tanımlanması
const DATABASE_CONFIG_FILENAME = ROOT ."../Data/database.json";
class DaoTest extends \Codeception\Test\Unit {
// TaxAdminData
private $taxAdminData;
public function __construct() {
parent::__construct();
// katman oluşturma [dao]
$dao = new DaoImpotsWithTaxAdminDataInDatabase(DATABASE_CONFIG_FILENAME);
$this->taxAdminData = $dao->getTaxAdminData();
}
// testler
public function testTaxAdminData() {
…
}
}
Açıklamalar
- 9-28. satırlar: test ortamının tanımlanması. Bağlantı paragrafında açıklanan ana komut dosyası [MainCalculateImpotsWithTaxAdminDataInPostgresDatabase]'te kullanılan ortamın aynısını, [métier] katmanı hariç, kullanıyoruz;
- 34-39. satırlar: [dao] katmanının oluşturulması;
- 38. satır: [$this→taxAdminData] özniteliği, test edilecek verileri içerir;
- 42-44. satırlar: [testTaxAdminData] yöntemi, bağlantı paragrafında açıklanan yöntemdir;
Test sonuçları şöyledir:

14.6.2. [métier] katmanının testi
[MetierTest.php] testi şu şekildedir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı uyumu
declare (strict_types=1);
// ad alanı
namespace Application;
// kök dizinler
define("ROOT", "C:/Data/st-2019/dev/php7/poly/scripts-console/impots/version-06");
define("VENDOR", "C:/myprograms/laragon-lite/www/vendor");
// arayüz ve sınıfların dahil edilmesi
require_once ROOT . "/../version-05/Entities/BaseEntity.php";
require_once ROOT . "/../version-05/Entities/TaxAdminData.php";
require_once ROOT . "/../version-05/Entities/TaxPayerData.php";
require_once ROOT . "/../version-05/Entities/Database.php";
require_once ROOT . "/../version-05/Entities/ExceptionImpots.php";
require_once ROOT . "/../version-05/Utilities/Utilitaires.php";
require_once ROOT . "/../version-05/Dao/InterfaceDao.php";
require_once ROOT . "/../version-05/Dao/TraitDao.php";
require_once ROOT . "/../version-05/Dao/DaoImpotsWithTaxAdminDataInDatabase.php";
require_once ROOT . "/../version-05/Métier/InterfaceMetier.php";
require_once ROOT . "/../version-05/Métier/Metier.php";
// üçüncü taraf kütüphaneler
require_once VENDOR . "/autoload.php";
// sabitlerin tanımlanması
const DATABASE_CONFIG_FILENAME = ROOT . "../Data/database.json";
class MetierTest extends \Codeception\Test\Unit {
// iş mantığı katmanı
private $métier;
public function __construct() {
parent::__construct();
// katman oluşturma [dao]
$dao = new DaoImpotsWithTaxAdminDataInDatabase(DATABASE_CONFIG_FILENAME);
// katman oluşturma [métier]
$this->métier = new Metier($dao);
}
// testler
public function test1() {
…
}
--------------------------------------------------------------------
public function test11() {
…
}
}
Açıklamalar
- 9-28. satırlar: test ortamının tanımlanması. Bağlantı paragrafında açıklanan ana komut dosyası [MainCalculateImpotsWithTaxAdminDataInPostgresDatabase] ile aynı ortamı kullanıyoruz;
- 34-40. satırlar: [dao] ve [métier] katmanlarının oluşturulması;
- 39. satır: [$this→métier] özniteliği, [métier] katmanına atıfta bulunur
- satır 43-49: [test1, test2…, test11] yöntemleri, bağlantı paragrafında açıklanan yöntemlerdir;
Test sonuçları şöyledir:
