13. Uygulama alıştırması – sürüm 5

Bu alıştırmanın daha önce birkaç versiyonunu yazmıştık. Son versiyonda katmanlı bir mimari kullanılmıştı:

[dao] katmanı, [InterfaceDao] arayüzünü uygular. Bu arayüzü uygulayan bir sınıf oluşturduk:
- [DaoImpotsWithTaxAdminDataInJsonFile]; bu sınıf, vergi verilerini jSON dosyasından alıyordu;
Şimdi, [InterfaceDao] arayüzünü, vergi idaresi verilerini MySQL veritabanından alacak olan yeni bir sınıf olan [DaoImpotsWithTaxAdminDataInDatabase] ile uygulayacağız.
13.1. [dbimpots-2019] veritabanının oluşturulması
Bağlantı paragrafındaki örneği takip ederek, sahibi [admimpots] ve şifresi [mdpimpots] olan, MySQL adlı bir veritabanı oluşturuyoruz:

- Yukarıdaki [1-4]'te, şimdilik herhangi bir tablosu olmayan [dbimpots-2019] veritabanını görüyoruz;

- Yukarıdaki [1-5]'te, [admimpots] kullanıcısının [dbimpots-2019] veritabanı üzerinde tüm haklara sahip olduğunu görüyoruz. Burada göremediğimiz şey ise bu kullanıcının şifresinin [admimpots] olduğudur;
Şimdi, vergi dilimlerini içerecek olan [tbtranches] tablosunu oluşturuyoruz:

- [1-7]'te, 4 sütunlu [tbtranches] adlı bir tablo oluşturuyoruz;

- [3-6]'te, [id] (3) adlı, tamsayı türünde bir sütun tanımlıyoruz; [int] (4) olan bir sütun tanımlıyoruz; bu sütun, tablonun birincil anahtarı [6] olacak ve SGBD tarafından otomatik olarak artırılacaktır [5]. Bu, MySQL'in ekleme işlemleri sırasında birincil anahtarın değerlerini kendisi yöneteceği anlamına gelir. İlk ekleme işleminde birincil anahtara 1 değerini, sonraki eklemede 2 değerini atayacak vb.;
- [7]'te ise sihirbaz, birincil anahtarın yapılandırılması için başka seçenekler sunar. Burada, [7]'te varsayılan değerleri onaylamakla yetiniyoruz;

- [8-16]'te, tablonun diğer üç sütunu tanımlanmaktadır:
- [limites] (8) on basamaklı (9), 2 ondalık basamaklı (10) ondalık sayı türünde (10) olan sütun, vergi dilimlerinin 17. sütunundaki öğeleri içerecektir;
- [coeffR] (11), ondalık sayı (12) türünde, 6 basamaklı (2'si ondalık) (13) olup, vergi dilimlerinin 18. sütunundaki öğeleri içerecektir;
- [coeffN] (14), 10 basamaklı (2'si ondalık) ondalık sayı (15) türünde olup, vergi dilimlerinin 19. sütunundaki öğeleri içerecektir;
Bu yapıyı onayladıktan sonra şu sonucu elde ederiz:

- [5]'te anahtar simgesi, [id] sütununun birincil anahtar olduğunu gösterir. Ayrıca bu birincil anahtarın tamsayı değerlere (6) sahip olduğu ve MySQL tarafından yönetildiği (otomatik olarak artırıldığı) görülmektedir;
[tbtranches] tablosunu oluşturduğumuz gibi, vergi hesaplamasına ilişkin sabitleri içerecek olan [tbconstantes] tablosunu da oluşturuyoruz:

Veritabanı yapısını, SQL komut dizisi biçiminde bir metin dosyasına aktarabilirsiniz:

[5] seçeneği burada yalnızca veritabanının yapısını dışa aktarır, içeriğini değil. Bizim durumumuzda, veritabanında henüz içerik bulunmamaktadır.



[11] seçeneği, aşağıdaki SQL [dbimpots-2019.sql] dosyasını oluşturur:
-- phpMyAdmin SQL Dump
-- sürüm 4.8.5
-- https://www.phpmyadmin.net/
--
-- Sunucu: localhost:3306
-- Oluşturulma Tarihi: 30 Haziran 2019, saat 01:10 PM
-- Sunucu sürümü: 5.7.24
-- PHP Sürümü: 7.2.11
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
--
-- Veritabanı: `dbimpots-2019`
--
CREATE DATABASE IF NOT EXISTS `dbimpots-2019` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `dbimpots-2019`;
-- --------------------------------------------------------
--
-- `tbconstantes` tablosunun tablo yapısı
--
DROP TABLE IF EXISTS `tbconstantes`;
CREATE TABLE `tbconstantes` (
`id` int(11) NOT NULL,
`plafondQfDemiPart` decimal(10,2) NOT NULL,
`plafondRevenusCelibatairePourReduction` decimal(10,2) NOT NULL,
`plafondRevenusCouplePourReduction` decimal(10,2) NOT NULL,
`valeurReducDemiPart` decimal(10,2) NOT NULL,
`plafondDecoteCelibataire` decimal(10,2) NOT NULL,
`plafondDecoteCouple` decimal(10,2) NOT NULL,
`plafondImpotCelibatairePourDecote` decimal(10,2) NOT NULL,
`plafondImpotCouplePourDecote` decimal(10,2) NOT NULL,
`abattementDixPourcentMax` decimal(10,2) NOT NULL,
`abattementDixPourcentMin` decimal(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- `tbtranches` tablosunun yapı bilgisi
--
DROP TABLE IF EXISTS `tbtranches`;
CREATE TABLE `tbtranches` (
`id` int(11) NOT NULL,
`limites` decimal(10,2) NOT NULL,
`coeffR` decimal(10,2) NOT NULL,
`coeffN` decimal(10,2) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Dökümlenen tablolar için dizinler
--
--
-- `tbconstantes` tablosu için dizinler
--
ALTER TABLE `tbconstantes`
ADD PRIMARY KEY (`id`);
--
-- `tbtranches` tablosu için dizinler
--
ALTER TABLE `tbtranches`
ADD PRIMARY KEY (`id`);
--
-- Dökümlenen tablolar için AUTO_INCREMENT
--
--
-- `tbconstantes` tablosu için AUTO_INCREMENT
--
ALTER TABLE `tbconstantes`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT, `tbtranches` tablosu için
--
ALTER TABLE `tbtranches`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
SQL dosyasını, [dbimpots-2019] veritabanı silinmiş veya bozulmuşsa, veritabanını yeniden oluşturmak için kullanabilirsiniz. SQL komut dosyası bunu kendisi hallettiği için, veritabanını yeniden oluşturmadan önce silmenize gerek yoktur:


13.2. Kodun Düzenlenmesi
Yazdığımız çeşitli PHP komut dosyalarının rolünü daha iyi göstermek için kodumuzu klasörler halinde düzenleyeceğiz:

- [1]'te, sürüm 05'e genel bakış;
- [2]'te, uygulamanın bileşenleri, katmanlar arasında paylaşılan bileşenler;
- [3]'e, uygulamanın yardımcı programları;
- [4] dosyasında, uygulama tarafından kullanılan veya üretilen veriler. Burada, metin dosyaları için yalnızca jSON dosyalarını kullanmaya karar verdik. Bu dosyaların birçok avantajı vardır:
- birçok araç tarafından tanınırlar;
- bu araçlar sözdizimi renklendirme özelliğine sahiptir. Ayrıca, jSON notasyonunun belirli kuralları vardır. Bu kurallara uyulmadığında araçlar bunu bildirir. Örneğin, basit bir metin dosyasında tespit edilmesi zor bir hata, sıfırların yerine büyük/küçük O harflerinin kullanılmasıdır. Bu hata meydana gelirse, uyarı verilir. Nitekim jSON kodunda:
"plafondRevenusCouplePourReduction": 42O74
burada [42074]'te yanlışlıkla sıfır yerine büyük O harfi kullanılmış; NetBeans bu hatayı bildirir:

Nitekim NetBeans, büyük O harfini tanıyarak [49O74]'i bir karakter dizisi olarak algılar. Bunun üzerine, sözdiziminin [4-5] olması gerektiği sonucuna varır: [47O74] dizisi tırnak işaretleri içine alınmalıdır. Böylece geliştiricinin dikkati hataya çekilir ve hatayı düzeltebilir: ya tırnak işaretleri ekleyebilir ya da O harfini sıfırla değiştirebilir;
05 sürümünün diğer öğeleri şunlardır:

- [6]'te, [Dao] katmanının arayüzleri ve sınıfları;
- [7]'e, [métier] katmanındaki arayüzler ve sınıflar;
- [8]'te, sürüm 05'in ana komut dosyaları;
Sürüm 05'in iki farklı amacı vardır:
- MySQL ve [dbimpots-2019] veritabanlarını jSON ve [Data/txadmindata.json] dosyalarının içeriğiyle doldurmak;
- vergi hesaplamasını, artık MySQL ve [dbimpots-2019] veritabanlarından alınan verilerle gerçekleştirmek;
Bu iki hedefi ayrı ayrı ele alacağız.
13.3. [dbimpots-2019] veritabanının doldurulması
13.3.1. Hedef
taxadmindata.json metin dosyası, vergi idaresine ait verileri içerir:
{
"limites": [
9964,
27519,
73779,
156244,
0
],
"coeffR": [
0,
0.14,
0.3,
0.41,
0.45
],
"coeffN": [
0,
1394.96,
5798,
13913.69,
20163.45
],
"plafondQfDemiPart": 1551,
"plafondRevenusCelibatairePourReduction": 21037,
"plafondRevenusCouplePourReduction": 42074,
"valeurReducDemiPart": 3797,
"plafondDecoteCelibataire": 1196,
"plafondDecoteCouple": 1970,
"plafondImpotCouplePourDecote": 2627,
"plafondImpotCelibatairePourDecote": 1595,
"abattementDixPourcentMax": 12502,
"abattementDixPourcentMin": 437
}
Hedefimiz, bu verileri daha önce oluşturulan MySQL [dbimpots-2019] veritabanına aktarmaktır.
13.3.2. Varlıklar

[Database] varlığı, aşağıdaki jSON ve [database.json] dosyalarındaki verileri kapsüllemek için kullanılacaktır:
{
"dsn": "mysql:host=localhost;dbname=dbimpots-2019",
"id": "admimpots",
"pwd": "mdpimpots",
"tableTranches": "tbtranches",
"colLimites": "limites",
"colCoeffR": "coeffr",
"colCoeffN": "coeffn",
"tableConstantes": "tbconstantes",
"colPlafondQfDemiPart": "plafondQfDemiPart",
"colPlafondRevenusCelibatairePourReduction": "plafondRevenusCelibatairePourReduction",
"colPlafondRevenusCouplePourReduction": "plafondRevenusCouplePourReduction",
"colValeurReducDemiPart": "valeurReducDemiPart",
"colPlafondDecoteCelibataire": "plafondDecoteCelibataire",
"colPlafondDecoteCouple": "plafondDecoteCouple",
"colPlafondImpotCelibatairePourDecote": "plafondImpotCelibatairePourDecote",
"colPlafondImpotCouplePourDecote": "plafondImpotCouplePourDecote",
"colAbattementDixPourcentMax": "abattementDixPourcentMax",
"colAbattementDixPourcentMin": "abattementDixPourcentMin"
}
[TaxAdminData] varlığı, aşağıdaki jSON ve [taxadmindata.json] dosyalarındaki verileri kapsüllemek için kullanılacaktır:
{
"limites": [
9964,
27519,
73779,
156244,
0
],
"coeffR": [
0,
0.14,
0.3,
0.41,
0.45
],
"coeffN": [
0,
1394.96,
5798,
13913.69,
20163.45
],
"plafondQfDemiPart": 1551,
"plafondRevenusCelibatairePourReduction": 21037,
"plafondRevenusCouplePourReduction": 42074,
"valeurReducDemiPart": 3797,
"plafondDecoteCelibataire": 1196,
"plafondDecoteCouple": 1970,
"plafondImpotCouplePourDecote": 2627,
"plafondImpotCelibatairePourDecote": 1595,
"abattementDixPourcentMax": 12502,
"abattementDixPourcentMin": 437
}
[TaxPayerData] varlığı, aşağıdaki jSON ve [taxpayerdata.json] dosyalarındaki verileri kapsüllemek için kullanılacaktır:
[
{
"marié": "oui",
"enfants": 2,
"salaire": 55555
},
{
"marié": "ouix",
"enfants": "2x",
"salaire": "55555x"
},
{
"marié": "oui",
"enfants": "2",
"salaire": 50000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 50000
},
{
"marié": "non",
"enfants": 2,
"salaire": 100000
},
{
"marié": "non",
"enfants": 3,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 5,
"salaire": 100000
},
{
"marié": "non",
"enfants": 0,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 2,
"salaire": 30000
},
{
"marié": "non",
"enfants": 0,
"salaire": 200000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 20000
}
]
13.3.2.1. [BaseEntity] temel sınıfı
Varlık kodunu basitleştirmek için şu kuralı uygulayacağız: Bir varlığın öznitelikleri, varlığın kapsamasını gerektiren jSON dosyasındaki özniteliklerle aynı isimlere sahiptir. Bu kural uyarınca, [Database, TaxAdminData, TaxPayerData] varlıklarının ortak özellikleri, bir üst sınıfta faktörlere ayrılabilir. Bu, aşağıdaki [BaseEntity] sınıfı olacaktır:
<?php
namespace Application;
class BaseEntity {
// özellik
protected $arrayOfAttributes;
// bir dosyadan başlatma jSON
public function setFromJsonFile(string $jsonFilename) {
// vergi verileri dosyasının içeriği alınır
$fileContents = \file_get_contents($jsonFilename);
$erreur = FALSE;
// hata mı?
if (!$fileContents) {
// hatkayı kaydediyoruz
$erreur = TRUE;
$message = "Le fichier des données [$jsonFilename] n'existe pas";
}
if (!$erreur) {
// yapılandırma dosyasından jSON kodunu bir ilişkisel tabloya aktarılır
$this->arrayOfAttributes = \json_decode($fileContents, true);
// hata mı?
if ($this->arrayOfAttributes === FALSE) {
// hata kaydediliyor
$erreur = TRUE;
$message = "Le fichier de données jSON [$jsonFilename] n'a pu être exploité correctement";
}
}
// hata mı?
if ($erreur) {
// bir istisna atılır
throw new ExceptionImpots($message);
}
// sınıf özniteliklerinin başlatılması
foreach ($this->arrayOfAttributes as $key => $value) {
$this->$key = $value;
}
// nesne döndürülüyor
return $this;
}
public function checkForAllAttributes() {
// tüm anahtarların başlatıldığını kontrol ediliyor
foreach (\array_keys($this->arrayOfAttributes) as $key) {
if ($key !== "arrayOfAttributes" && !isset($this->$key)) {
throw new ExceptionImpots("L'attribut [$key] de la classe "
. get_class($this) . " n'a pas été initialisé");
}
}
}
public function setFromArrayOfAttributes(array $arrayOfAttributes) {
// sınıfın belirli özellikleri başlatılıyor
foreach ($arrayOfAttributes as $key => $value) {
$this->$key = $value;
}
// nesneyi döndürme
return $this;
}
// toString
public function __toString() {
// nesnenin öznitelikleri
$arrayOfAttributes = \get_object_vars($this);
// üst sınıftaki özniteliği kaldırıyoruz
unset($arrayOfAttributes["arrayOfAttributes"]);
// nesnenin JSON dizesi
return \json_encode($arrayOfAttributes, JSON_UNESCAPED_UNICODE);
}
// getter
public function getArrayOfAttributes() {
return $this->arrayOfAttributes;
}
}
Açıklamalar
- 5. satır: [BaseEntity] sınıfı, [Database, TaxAdminData, TaxPayerData] sınıfları tarafından genişletilmek üzere tasarlanmıştır;
- 7. satır: [$arrayOfAttributes] özniteliği, [BaseEntity] sınıfını genişleten tüm alt sınıf özniteliklerini ve bunların değerlerini içeren bir dizidir;
- 9-41. satırlar: [$arrayOfAttributes] özniteliği, parametre olarak geçirilen jSON ve [$jsonFilename] dosyalarından başlatılır. jSON dosyası okunamadıysa veya geçerli bir jSON dosyası değilse, [ExceptionImpot] türünde bir istisna atılır;
- 36-38. satırlar: Bu kod, bir alt sınıf tarafından yürütülürse özel bir durum oluşturur. Bu durumda, [$this], alt sınıf [Database, TaxAdminData, TaxPayerData]'in bir örneğini temsil eder ve bu durumda, 36-38. satırlar, bu alt sınıfın özniteliklerini, söz konusu özniteliklerin görünürlük düzeyinin protected (veya public) olması koşuluyla başlatır (bkz. bağlantı paragrafı). Nitekim, [Database, TaxAdminData, TaxPayerData] varlıklarının özniteliklerinin, kapsadıkları jSON dosyasının öznitelikleriyle aynı olduğu belirtilmişti. Son olarak, [setFromJsonFile] yöntemi, bir alt sınıfın jSON dosyasından başlatılmasını sağlar;
- 40. satır: [$this] nesnesi, yani bir alt sınıf örneği, [setFromJsonFile] yöntemi bir alt sınıf tarafından çağrılmışsa döndürülür;
- 43-51. satırlar: [checkForAllAttributes] yöntemi, bir alt sınıfın tüm özniteliklerinin başlatılmış olup olmadığını kontrol etmesini sağlar. Aksi takdirde, bir [ExceptionImpots] istisnası atılır. Bu yöntem, alt sınıfın jSON dosyasının bazı öznitelikleri atlamadığını kontrol etmesini sağlar;
- 53-60. satırlar: [setFromArrayOfAttributes] yöntemi, bir alt sınıfın özniteliklerinin tamamını veya bir kısmını, anahtarları başlatılacak alt sınıfın öznitelikleriyle aynı isimlere sahip olan bir ilişkisel diziden başlatmasına olanak tanır;
- satır 63-70: [__toString] yöntemi, bir alt sınıfın jSON temsilini elde etmeyi sağlar;
13.3.2.2. [Database] varlığı
[Database] varlığı şöyledir:
<?php
namespace Application;
class Database extends BaseEntity {
// özellikler
protected $dsn;
protected $id;
protected $pwd;
protected $tableTranches;
protected $colLimites;
protected $colCoeffR;
protected $colCoeffN;
protected $tableConstantes;
protected $colPlafondQfDemiPart;
protected $colPlafondRevenusCelibatairePourReduction;
protected $colPlafondRevenusCouplePourReduction;
protected $colValeurReducDemiPart;
protected $colPlafondDecoteCelibataire;
protected $colPlafondDecoteCouple;
protected $colPlafondImpotCelibatairePourDecote;
protected $colPlafondImpotCouplePourDecote;
protected $colAbattementDixPourcentMax;
protected $colAbattementDixPourcentMin;
…
}
[Database] sınıfı, aşağıdaki jSON ve [database.json] dosyalarındaki verileri kapsüllemek için kullanılır:
{
"dsn": "mysql:host=localhost;dbname=dbimpots-2019",
"id": "admimpots",
"pwd": "mdpimpots",
"tableTranches": "tbtranches",
"colLimites": "limites",
"colCoeffR": "coeffr",
"colCoeffN": "coeffn",
"tableConstantes": "tbconstantes",
"colPlafondQfDemiPart": "plafondQfDemiPart",
"colPlafondRevenusCelibatairePourReduction": "plafondRevenusCelibatairePourReduction",
"colPlafondRevenusCouplePourReduction": "plafondRevenusCouplePourReduction",
"colValeurReducDemiPart": "valeurReducDemiPart",
"colPlafondDecoteCelibataire": "plafondDecoteCelibataire",
"colPlafondDecoteCouple": "plafondDecoteCouple",
"colPlafondImpotCelibatairePourDecote": "plafondImpotCelibatairePourDecote",
"colPlafondImpotCouplePourDecote": "plafondImpotCouplePourDecote",
"colAbattementDixPourcentMax": "abattementDixPourcentMax",
"colAbattementDixPourcentMin": "abattementDixPourcentMin"
}
jSON sınıfı ve dosyası aynı özniteliklere sahiptir. Bu öznitelikler, MySQL ve [dbimpots-2019] veritabanlarının özelliklerini tanımlar:
dsn | Veritabanının adı: DSN |
id | Veritabanı sahibi |
pwd | Şifresi |
tableTranches | Vergi dilimlerini içeren tablonun adı |
colLimites colCoeffR colCoeffN | [tableTranches] tablosundaki sütun adları |
tableConstantes | Vergi hesaplama sabitlerini içeren tablonun adı |
colPlafondQfDemiPart colPlafondRevenusCelibatairePourReduction colPlafondRevenusCouplePourReduction colValeurReducDemiPart colPlafondDecoteCelibataire colPlafondDecoteCouple colPlafondImpotCelibatairePourDecote colPlafondImpotCouplePourDecote colAbattementDixPourcentMax colAbattementDixPourcentMin | Vergi hesaplama sabitlerini içeren [tableConstantes] tablosundaki sütun adları |
Adlarını zaten bildiğimiz ve değişmesi de beklenmeyen tabloları ve sütunları neden adlandırıyoruz? SGBD ve MySQL'ten sonra, vergi idaresi verilerini depolamak için SGBD ve PostgreSQL'i kullanacağız. Ancak Postgres sütun ve tablo adları, MySQL ile aynı kurallara uymuyor. Başka isimler kullanmak zorunda kalacağız. Bu durum diğer SGBD'ler için de geçerlidir. SGBD arasında taşınabilir kod elde etmek istiyorsak, tablo ve sütunların sabit isimleri yerine parametreler kullanmak daha uygun olacaktır.
[Database] sınıfının koduna geri dönelim:
<?php
namespace Application;
class Database extends BaseEntity {
// özellikler
protected $dsn;
protected $id;
protected $pwd;
protected $tableTranches;
protected $colLimites;
protected $colCoeffR;
protected $colCoeffN;
protected $tableConstantes;
protected $colPlafondQfDemiPart;
protected $colPlafondRevenusCelibatairePourReduction;
protected $colPlafondRevenusCouplePourReduction;
protected $colValeurReducDemiPart;
protected $colPlafondDecoteCelibataire;
protected $colPlafondDecoteCouple;
protected $colPlafondImpotCelibatairePourDecote;
protected $colPlafondImpotCouplePourDecote;
protected $colAbattementDixPourcentMax;
protected $colAbattementDixPourcentMin;
// setter
// başlatma
public function setFromJsonFile(string $jsonFilename) {
// üst nesne
parent::setFromJsonFile($jsonFilename);
// tüm özniteliklerin başlatıldığını kontrol eder
parent::checkForAllAttributes();
// nesne döndürülür
return $this;
}
// alıcı ve ayarlayıcılar
public function getDsn() {
return $this->dsn;
}
…
public function setDsn($dsn) {
$this->dsn = $dsn;
return $this;
}
…
}
Açıklamalar
- 7-24. satırlar: Sınıfın tüm özniteliklerinin görünürlüğü [protected]'tir. Bu, bunların üst sınıf [BaseEntity]'ten değiştirilebilmesi için gerekli koşuldur (bkz. bağlantı paragrafı);
- 28-35. satırlar: [setFromJsonFile] yöntemi, parametre olarak geçirilen bir jSON dosyasının içeriğinden [Database] sınıfının özniteliklerini başlatmaya olanak tanır. jSON dosyasının öznitelikleri ile [Database] sınıfının öznitelikleri aynı olmalıdır. jSON dosyası kullanılamaz durumdaysa, bir istisna atılır;
- 30. satır: Başlatma işlemini üst sınıf gerçekleştirir;
- 32. satır: üst sınıftan, [Database] sınıfının tüm özniteliklerinin başlatılmış olup olmadığını kontrol etmesi istenir. Aksi takdirde bir istisna atılır;
- satır 34: az önce başlatılan [Database] örneği döndürülür;
- 37. satır ve sonrası: sınıfın özniteliklerine ait getter ve setter yöntemleri;
13.3.2.3. [TaxAdminData] nesnesi
[TaxAdminData] varlığı şöyledir:
<?php
namespace Application;
class TaxAdminData extends BaseEntity {
// vergi dilimleri
protected $limites;
protected $coeffR;
protected $coeffN;
// vergi hesaplama sabitleri
protected $plafondQfDemiPart;
protected $plafondRevenusCelibatairePourReduction;
protected $plafondRevenusCouplePourReduction;
protected $valeurReducDemiPart;
protected $plafondDecoteCelibataire;
protected $plafondDecoteCouple;
protected $plafondImpotCouplePourDecote;
protected $plafondImpotCelibatairePourDecote;
protected $abattementDixPourcentMax;
protected $abattementDixPourcentMin;
…
}
[TaxAdminData] sınıfı, aşağıdaki jSON ve [taxadmindata.json] dosyalarındaki verileri sarmalamak için kullanılır:
{
"limites": [
9964,
27519,
73779,
156244,
0
],
"coeffR": [
0,
0.14,
0.3,
0.41,
0.45
],
"coeffN": [
0,
1394.96,
5798,
13913.69,
20163.45
],
"plafondQfDemiPart": 1551,
"plafondRevenusCelibatairePourReduction": 21037,
"plafondRevenusCouplePourReduction": 42074,
"valeurReducDemiPart": 3797,
"plafondDecoteCelibataire": 1196,
"plafondDecoteCouple": 1970,
"plafondImpotCouplePourDecote": 2627,
"plafondImpotCelibatairePourDecote": 1595,
"abattementDixPourcentMax": 12502,
"abattementDixPourcentMin": 437
}
jSON sınıfı ve dosyası aynı özniteliklere sahiptir. Bunlar, vergi idaresine ait verileri temsil eder. [TaxAdminData] sınıfının kodunun geri kalanı şöyledir:
<?php
namespace Application;
class TaxAdminData extends BaseEntity {
// vergi dilimleri
protected $limites;
protected $coeffR;
protected $coeffN;
// vergi hesaplama sabitleri
protected $plafondQfDemiPart;
protected $plafondRevenusCelibatairePourReduction;
protected $plafondRevenusCouplePourReduction;
protected $valeurReducDemiPart;
protected $plafondDecoteCelibataire;
protected $plafondDecoteCouple;
protected $plafondImpotCouplePourDecote;
protected $plafondImpotCelibatairePourDecote;
protected $abattementDixPourcentMax;
protected $abattementDixPourcentMin;
// başlatma
public function setFromJsonFile(string $taxAdminDataFilename) {
// üst öğe
parent::setFromJsonFile($taxAdminDataFilename);
// tüm özniteliklerin başlatılmış olduğu kontrol edilir
parent::checkForAllAttributes();
// özelliklerin değerlerinin >=0 olan gerçek sayılar olduğu kontrol edilir
foreach ($this as $key => $value) {
if ($key !== "arrayOfAttributes") {
// $value, >=0 olan bir gerçek sayı veya >=0 olan gerçek sayılardan oluşan bir dizi olmalıdır
$result = $this->check($value);
// hata mı?
if ($result->erreur) {
// bir istisna atılır
throw new ExceptionImpots("La valeur de l'attribut [$key] est invalide");
} else {
// değer kaydediliyor
$this->$key = $result->value;
}
}
}
// nesne döndürülüyor
return $this;
}
protected function check($value): \stdClass {
// $value, string türünde elemanlardan oluşan bir dizi veya tek bir elemandır
if (!\is_array($value)) {
$tableau = [$value];
} else {
$tableau = $value;
}
// string dizisini gerçek sayı dizisine dönüştürülür
$newTableau = [];
$result = new \stdClass();
// dizinin elemanları pozitif veya sıfır olan ondalık sayılar olmalıdır
$modèle = '/^\s*([+]?)\s*(\d+\.\d*|\.\d+|\d+)\s*$/';
for ($i = 0; $i < count($tableau); $i ++) {
if (preg_match($modèle, $tableau[$i])) {
// float değerini newTableau'e yerleştirilir
$newTableau[] = (float) $tableau[$i];
} else {
// hatayı not ediyoruz
$result->erreur = TRUE;
// çıkılır
return $result;
}
}
// sonucu döndürürüz
$result->erreur = FALSE;
if (!\is_array($value)) {
// tek bir değer
$result->value = $newTableau[0];
} else {
// bir değer listesi
$result->value = $newTableau;
}
return $result;
}
// alıcı ve ayarlayıcılar
…
}
Açıklamalar
- 23. satır: [setFromJsonFile] yöntemi, parametre olarak geçirilen jSON dosyasından [TaxAdminData] sınıfının özniteliklerini başlatmak için kullanılır. jSON dosyasındaki özniteliklerin sınıfta da aynı adla mevcut olması gerekir;
- 25. satır: Bu işlemi üst sınıf gerçekleştirir;
- 27. satır: üst sınıftan, alt sınıfın tüm özniteliklerinin başlatıldığını doğrulaması istenir;
- 29-42. satırlar: tüm özniteliklerin pozitif bir gerçek değer veya sıfır değerine sahip olup olmadığı yerel olarak kontrol edilir. Bu kontrol, sürüm 03'ün "bağlantı" paragrafında daha önce ele alınmıştır;
13.3.3. [dao] katmanı
Artık [taxadmindata.json] metin dosyasındaki verileri MySQL ve [dbimpots-2019] veritabanlarındaki [tbtranches, tbconstantes] tablolarına aktaracak kodu yazabiliriz. Aşağıdaki mimariyi benimseyeceğiz:


[dao] katmanı, aşağıdaki [InterfaceDao4TransferAdminDataFromFile2Database] arayüzünü uygulayacaktır:
<?php
// ad alanı
namespace Application;
interface InterfaceDao4TransferAdminData2Database {
public function transferAdminData2Database(): void;
}
Açıklamalar
- 8. satır: [transferAdminData2Database] yöntemi, vergi idaresine ait verileri bir veritabanında depolamakla görevlidir;
[InterfaceDao4TransferAdminData2Database] arayüzü, aşağıdaki [DaoTransferAdminDataFromJsonFile2Database] sınıfı tarafından uygulanacaktır:
<?php
// ad alanı
namespace Application;
// bir sınıfın tanımı TransferAdminDataFromFile2DatabaseDao
class DaoTransferAdminDataFromJsonFile2Database implements InterfaceDao4TransferAdminData2Database {
// hedef veritabanının öznitelikleri
private $database;
// vergi idaresi verileri
private $taxAdminData;
// üretici
public function __construct(string $databaseFilename, string $taxAdminDataFilename) {
// veritabanı yapılandırması kaydedilir
$this->database = (new Database())->setFromJsonFile($databaseFilename);
// vergi verileri kaydedilir
$this->taxAdminData = (new TaxAdminData())->setFromJsonFile($taxAdminDataFilename);
}
// vergi dilimlerine ait verileri bir metin dosyasından aktarır
// veritabanına aktarılır
public function transferAdminData2Database(): void {
// veritabanı üzerinde çalışılır
$database = $this->database;
try {
// veritabanına bağlantı kurulur
$connexion = new \PDO($database->getDsn(), $database->getId(), $database->getPwd());
// SGBD'te her hata durumunda bir istisna atılmasını istiyoruz
$connexion->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
// bir işlem başlatıyoruz
$connexion->beginTransaction();
// vergi dilimleri tablosu dolduruluyor
$this->fillTableTranches($connexion);
// sabitler tablosu doldurulur
$this->fillTableConstantes($connexion);
// işlem başarıyla sonlandırılır
$connexion->commit();
} catch (\PDOException $ex) {
// devam eden bir işlem var mı?
if (isset($connexion) && $connexion->inTransaction()) {
// işlem başarısızlıkla sonlandırılır
$connexion->rollBack();
}
// istisna, çağıran koda iletilir
throw new ExceptionImpots($ex->getMessage());
} finally {
// bağlantı kapatılıyor
$connexion = NULL;
}
}
// vergi dilimleri tablosu dolduruluyor
private function fillTableTranches($connexion): void {
…
}
// sabitler tablosu dolduruluyor
private function fillTableConstantes($connexion): void {
…
}
}
Açıklamalar
Burada, MySQL ile ilgili bölümde öğrendiklerimizi kullanıyoruz.
- 7. satır: [DaoTransferAdminDataFromJsonFile2Database] sınıfı, [InterfaceDao4TransferAdminData2Database] arayüzünü uygular;
- 9. satır: [$database] özniteliği, [database.json] dosyasındaki verileri kapsayan [Database] türündeki nesnedir;
- 11. satır: [$taxAdminData] özniteliği, [taxadmindata.json] dosyasının verilerini kapsayan [TaxAdminData] türündeki nesnedir;
- 14-19. satırlar: oluşturucu, [database.json, taxadmindata.json] dosyalarının adlarını parametre olarak alır;
- 16. satır: [$database] özniteliğinin başlatılması;
- satır 18: [$taxAdminData] özniteliğinin başlatılması;
- 23. satır: [InterfaceDao4TransferAdminData2Database] arayüzünün tek yöntemi uygulanır;
- satır 26-38: [tbtranches, tbconstantes] tabloları iki aşamada doldurulur:
- satır 34: önce [tbtranches] tablosu doldurulur. Bu işlem bir işlem içinde gerçekleştirilir (satır 32, 38). [fillTableTranches] yöntemi (satır 55), herhangi bir sorun oluştuğunda bir istisna atar. Bu durumda, yürütme 39-50. satırlardaki catch / finally bloğu ile devam eder;
- satır 36: [tbconstantes] tablosu, [fillTableConstantes] yöntemi (satır 60) kullanılarak aynı şekilde doldurulur;
- 39-47. satırlar: kod tarafından bir istisna atıldığı durum;
- 41-44. satırlar: Bir işlem varsa, bu işlem iptal edilir;
- 46. satır: [ExceptionImpots] türünde bir istisna atılır; bu istisnanın orijinal mesajı ise herhangi bir türdedir;
- 47-50. satırlar: [finally] cümlesinde bağlantı kapatılır;
[fillTableTranches] yönteminin kodu şöyledir:
private function fillTableTranches($connexion): void {
// veritabanı kısayolu
$database = $this->database;
// veritabanına eklenecek veriler
$limites = $this->taxAdminData->getLimites();
$coeffR = $this->taxAdminData->getCoeffR();
$coeffN = $this->taxAdminData->getCoeffN();
// içinde bir şey varsa tablonun boşaltılması
$statement = $connexion->prepare("delete from " . $database->getTableTranches());
$statement->execute();
// ekleme işlemlerini hazırlıyoruz
$sqlInsert = "insert into {$database->getTableTranches()} "
. "({$database->getColLimites()}, {$database->getColCoeffR()},"
. " {$database->getColCoeffN()}) values (:limites, :coeffR, :coeffN)";
$statement = $connexion->prepare($sqlInsert);
// vergi dilimlerinin değerleriyle hazırlanan komutu çalıştırıyoruz
for ($i = 0; $i < count($limites); $i++) {
$statement->execute([
"limites" => $limites[$i],
"coeffR" => $coeffR[$i],
"coeffN" => $coeffN[$i]]);
}
}
Açıklamalar
- 1. satır: [fillTableTranches] yöntemi, parametre olarak açık bir bağlantı alır. Ayrıca, bu bağlantı içinde bir işlemin başlatıldığı bilinmektedir;
- satır 5-7: tabloya eklenecek değerler, [$taxAdminData] özniteliği tarafından sağlanır;
- 9-10. satırlar: [tbtranches] tablosunun mevcut içeriği silinir;
- satır 12-15: tabloya satırların eklenmesi için hazırlık yapılır. Burada, [$database] özniteliği tarafından sağlanan sütun adları kullanılır;
- satır 17-22: 12-15. satırlarda hazırlanan ekleme komutu, gerektiği kadar çok kez yürütülür;
[fillTableConstantes] yönteminin kodu şöyledir:
private function fillTableConstantes($connexion): void {
// kısayol
$database = $this->database;
// içinde bir şey olması ihtimaline karşı tabloyu boşaltıyoruz
$statement = $connexion->prepare("delete from {$database->getTableConstantes()}");
$statement->execute();
// eklemeyi hazırlıyoruz
$taxAdminData = $this->taxAdminData;
$sqlInsert = "insert into {$database->getTableConstantes()}"
. " ({$database->getColPlafondQfDemiPart()},"
. " {$database->getColPlafondRevenusCelibatairePourReduction()},"
. " {$database->getColPlafondRevenusCouplePourReduction()},"
. " {$database->getColValeurReducDemiPart()},"
. " {$database->getColPlafondDecoteCelibataire()},"
. " {$database->getColPlafondDecoteCouple()},"
. " {$database->getColPlafondImpotCelibatairePourDecote()},"
. " {$database->getColPlafondImpotCouplePourDecote()},"
. " {$database->getColAbattementDixPourcentMax()},"
. " {$database->getColAbattementDixPourcentMin()})"
. " values ("
. ":plafondQfDemiPart,"
. ":plafondRevenusCelibatairePourReduction,"
. ":plafondRevenusCouplePourReduction,"
. ":valeurReducDemiPart,"
. ":plafondDecoteCelibataire,"
. ":plafondDecoteCouple,"
. ":plafondImpotCelibatairePourDecote,"
. ":plafondImpotCouplePourDecote,"
. ":abattementDixPourcentMax,"
. ":abattementDixPourcentMin)";
$statement = $connexion->prepare($sqlInsert);
// hazırlanan komut çalıştırılır
$statement->execute([
"plafondQfDemiPart" => $taxAdminData->getPlafondQfDemiPart(),
"plafondRevenusCelibatairePourReduction" => $taxAdminData->getPlafondRevenusCelibatairePourReduction(),
"plafondRevenusCouplePourReduction" => $taxAdminData->getPlafondRevenusCouplePourReduction(),
"valeurReducDemiPart" => $taxAdminData->getValeurReducDemiPart(),
"plafondDecoteCelibataire" => $taxAdminData->getPlafondDecoteCelibataire(),
"plafondDecoteCouple" => $taxAdminData->getPlafondDecoteCouple(),
"plafondImpotCelibatairePourDecote" => $taxAdminData->getPlafondImpotCelibatairePourDecote(),
"plafondImpotCouplePourDecote" => $taxAdminData->getPlafondImpotCouplePourDecote(),
"abattementDixPourcentMax" => $taxAdminData->getAbattementDixPourcentMax(),
"abattementDixPourcentMin" => $taxAdminData->getAbattementDixPourcentMin()
]);
}
Açıklamalar
- 1. satır: [fillTableConstantes] yöntemi, açık bir bağlantı parametresi alır. Ayrıca, bu bağlantı içinde bir işlemin başlatıldığı bilinmektedir;
- 5-6. satırlar: [tbconstantes] tablosu boşaltılır;
- satır 9-31: SQL ekleme emrinin hazırlanması. Bu ekleme işleminde başlatılması gereken 10 sütun olması ve sütun adlarının [$database] özniteliğinden alınması gerektiği için kod karmaşıktır;
- satır 33-44: Ekleme emrinin yürütülmesi. Eklenecek tek bir satır vardır. Burada da, eklenecek değerlerin [$taxAdminData] özniteliğinden alınması gerektiği için kod karmaşıktır;
13.3.4. Ana komut dosyası


Ana komut dosyası, veri aktarımını gerçekleştirmek için [dao] katmanını kullanır:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı sıkıya uyulur
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__ . "/../Entities/BaseEntity.php";
require_once __DIR__ . "/../Entities/TaxAdminData.php";
require_once __DIR__ . "/../Entities/TaxPayerData.php";
require_once __DIR__ . "/../Entities/Database.php";
require_once __DIR__ . "/../Entities/ExceptionImpots.php";
require_once __DIR__ . "/../Utilities/Utilitaires.php";
require_once __DIR__ . "/../Dao/InterfaceDao.php";
require_once __DIR__ . "/../Dao/TraitDao.php";
require_once __DIR__ . "/../Dao/InterfaceDao4TransferAdminData2Database.php";
require_once __DIR__ . "/../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
- 12-21. satırlar: uygulamanın sınıflarının ve arayüzlerinin yüklenmesi;
- 24-24. satırlar: iki jSON dosyası;
- 30. satır: [dao] katmanını, iki jSON dosyasını yapıcıya aktararak örneklendiriyoruz;
- satır 32: veri aktarımı gerçekleştirilir;
Bu kodu çalıştırdığımızda, veritabanında şu sonucu elde ederiz:

[3] sütununda, MySQL tarafından [id] birincil anahtarına atanan değerler görülmektedir. Numaralandırma 1'den başlar. Yukarıdaki ekran görüntüsü, komut dosyasının birkaç kez çalıştırılmasının ardından elde edilmiştir.


13.4. Vergi Hesaplaması

13.4.1. Mimari
Vergi hesaplama uygulamasının 04 sürümü, katmanlı bir mimari kullanıyordu:

[dao] katmanı, [InterfaceDao] arayüzünü uygular. Bu arayüzü uygulayan bir sınıf oluşturduk:
- [DaoImpotsWithTaxAdminDataInJsonFile]; bu sınıf, vergi verilerini jSON dosyasından alıyordu. Bu, 04 sürümüydü;
[InterfaceDao] arayüzünü, vergi idaresinin verilerini MySQL veritabanından alacak olan yeni bir [DaoImpotsWithTaxAdminDataInDatabase] sınıfı aracılığıyla uygulayacağız. [dao] katmanı, daha önce olduğu gibi, sonuçları ve hataları metin dosyalarına yazacak ve vergi mükelleflerinin verilerini de bir metin dosyasından alacaktır. Ancak bu sefer, bu metin dosyaları jSON dosyaları olacaktır. Ayrıca, [InterfaceDao] arayüzüne uymaya devam edersek, [métier] katmanının değiştirilmesine gerek kalmayacağını biliyoruz.

13.4.2. [TaxPayerData] varlığı

[TaxPayerData] sınıfı, aşağıdaki jSON ve [taxpayersdata.json] dosyalarındaki verileri bir sınıf içinde kapsüllemek için kullanılır:
[
{
"marié": "oui",
"enfants": 2,
"salaire": 55555
},
{
"marié": "ouix",
"enfants": "2x",
"salaire": "55555x"
},
{
"marié": "oui",
"enfants": "2",
"salaire": 50000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 50000
},
{
"marié": "non",
"enfants": 2,
"salaire": 100000
},
{
"marié": "non",
"enfants": 3,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 5,
"salaire": 100000
},
{
"marié": "non",
"enfants": 0,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 2,
"salaire": 30000
},
{
"marié": "non",
"enfants": 0,
"salaire": 200000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 20000
}
]
[TaxPayerData] sınıfı aşağıdaki gibidir:
<?php
// ad alanı
namespace Application;
// veri sınıfı
class TaxPayerData extends BaseEntity {
// vergi mükellefinin vergisinin hesaplanması için gerekli veriler
protected $marié;
protected $enfants;
protected $salaire;
// vergi hesaplama sonuçları
protected $impôt;
protected $surcôte;
protected $décôte;
protected $réduction;
protected $taux;
// alıcı ve ayarlayıcılar
…
}
Açıklamalar
- 7. satır: [TaxPayerData] sınıfı, [BaseEntity] sınıfını genişletir. Üst sınıfının yöntemleri yeterli olduğundan, [TaxPayerData] sınıfı kendisi için yeni yöntem tanımlamaz. [TaxPayerData] sınıfının özniteliklerinin, jSON ve [taxpayersdata.json] dosyalarındakilerle aynı olduğu hatırlatılmalıdır;
13.4.3. [dao] katmanı
13.4.3.1. [TraitDao] özelliği
[TraitDao] özelliği, [InterfaceDao] arayüzünün bir kısmını uygular. Bu arayüzü hatırlayalım:
<?php
// ad alanı
namespace Application;
interface InterfaceDao {
// vergi mükellefi verilerinin okunması
public function getTaxPayersData(string $taxPayersFilename, string $errorsFilename): array;
// vergi idaresi verilerinin okunması (vergi dilimleri)
public function getTaxAdminData(): TaxAdminData;
// sonuçların kaydedilmesi
public function saveResults(string $resultsFilename, array $taxPayersData): void;
}
[TraitDao] özelliği, [InterfaceDao] arayüzünün [getTaxPayersData, saveResults] yöntemlerini uygular. 04 ve 05 sürümleri arasında [TaxPayerData] varlığının tanımı değiştirildiği için, [TraitDao] kodunu gözden geçirmemiz gerekiyor:
<?php
// ad alanı
namespace Application;
trait TraitDao {
// vergi mükellefi verilerinin okunması
public function getTaxPayersData(string $taxPayersFilename, string $errorsFilename): array {
// vergi mükelleflerinin verileri bir tabloya alınır
$baseEntity = new BaseEntity();
$baseEntity->setFromJsonFile($taxPayersFilename);
$arrayOfAttributes = $baseEntity->getArrayOfAttributes();
// vergi mükellefi verileri tablosu
$taxPayersData = [];
// hata tablosu
$errors = [];
// [TaxPayerData] türündeki öğelerin öznitelik tablosu üzerinde döngü
$i = 0;
foreach ($arrayOfAttributes as $attributesOfTaxPayerData) {
// doğrulama
$error = $this->check($attributesOfTaxPayerData);
if (!$error) {
// bir vergi mükellefi +
$taxPayersData[] = (new TaxPayerData())->setFrOmArrayOfAttributes($attributesOfTaxPayerData);
} else {
// bir hata - geçersiz verinin numarası not edilir
$error = ["numéro" => $i] + $error;
$errors[] = $error;
}
// sonraki
$i++;
}
// hatalar bir json dosyasına kaydedilir
$string = "";
foreach ($errors as $error) {
$string .= \json_encode($error, JSON_UNESCAPED_UNICODE) . "\n";
}
$this->saveString($errorsFilename, $string);
// fonksiyonun sonucu
return $taxPayersData;
}
private function check(array $attributesOfTaxPayerData): array {
// [$taxPayerData] verileri kontrol ediliyor
// hatalı özniteliklerin listesi
$attributes = [];
// medeni durum "evet" veya "hayır" olmalıdır
$marié = trim(strtolower($attributesOfTaxPayerData["marié"]));
$erreur = ($marié !== "oui" and $marié !== "non");
if ($erreur) {
// hata not ediliyor
$attributes[] = ["marié" => $marié];
}
// çocuk sayısı pozitif bir tamsayı veya sıfır olmalıdır
$enfants = trim($attributesOfTaxPayerData["enfants"]);
if (!preg_match("/^\d+$/", $enfants)) {
// hata kaydediliyor
$erreur = TRUE;
$attributes[] = ["enfants" => $enfants];
} else {
$enfants = (int) $enfants;
}
// Maaş, pozitif bir tamsayı veya sıfır olmalıdır (euro sentleri hariç)
$salaire = trim($attributesOfTaxPayerData["salaire"]);
if (!preg_match("/^\d+$/", $salaire)) {
// hata kaydedildi
$erreur = TRUE;
$attributes[] = ["salaire" => $salaire];
} else {
$salaire = (int) $salaire;
}
// hata mı?
if ($erreur) {
// hata ile geri dönüş
return ["erreurs" => $attributes];
} else {
// hatasız dönüş
return [];
}
}
// sonuçların kaydedilmesi
public function saveResults(string $resultsFilename, array $taxPayersData): void {
// [$taxPayersData] tablosunun [$resultsFileName] metin dosyasına kaydedilmesi
// [$resultsFileName] metin dosyası yoksa, bu dosya oluşturulur
// sonuçlardan jSON dizesinin oluşturulması
$string = "[" . implode(",
", $taxPayersData) . "]";
// bu dizenin kaydedilmesi
$this->saveString($resultsFilename, $string);
}
// bir tablodaki sonuçların bir metin dosyasına kaydedilmesi
private function saveString(string $fileName, string $data): void {
// [$data] dizesinin [$fileName] metin dosyasına kaydedilmesi
// [$fileName] metin dosyası yoksa, oluşturulur
if (file_put_contents($fileName, $data) === FALSE) {
throw new ExceptionImpots("Erreur lors de l'enregistrement de données dans le fichier texte [$fileName]");
}
}
}
Açıklamalar
- [TraitDao], [InterfaceDao] arayüzünün [getTaxPayersData] (9. satır) ve [saveResults] (86. satır) yöntemlerini uygular;
- 9. satır: [getTaxPayersData] yöntemi şu parametreleri alır:
- [$taxPayersFilename]: jSON verilerindeki vergi mükelleflerinin [taxpayersdata.json] dosya adı;
- [$errorsFilename]: hataların bulunduğu jSON dosyasının adı [errors.json];
- 11-13. satırlar: vergi mükelleflerine ait verilerin bulunduğu jSON dosyasının içeriği, [$arrayOfAttributes] adlı bir ilişkisel tabloya aktarılır. jSON dosyası işlenemez durumdaysa, [ExceptionImpots] istisnası tetiklenmiştir;
- 15. satır: [$taxPayersData] tablosu, [TaxPayerData] türündeki nesneler içinde kapsüllenmiş vergi mükellefi verilerini içerecektir;
- 17. satır: Hatalar [$errors] tablosunda toplanacaktır;
- 99-33. satırlar: [$taxPayersData] tablosunun oluşturulması;
- 22. satır: Veriler, [TaxPayerData] türüne kapsüllenmeden önce doğrulanır. [check] yöntemi,
- veriler hatalıysa, hatalı öznitelikleri içeren bir [‘erreurs’=>[…]] dizisi;
- veriler doğruysa boş bir dizi;
- 25. satır: verilerin geçerli olduğu durum. Yeni bir [TaxPayerData] nesnesi oluşturulur ve [$taxPayersData] dizisine eklenir;
- 26-30. satırlar: verilerin geçersiz olduğu durum. Hata mesajında, kullanıcının bulabilmesi için jSON dosyasındaki hatalı [TaxPayerData] nesnesinin numarası belirtilir, ardından hata [$errors] tablosuna eklenir;
- 35-39. satırlar: parametre olarak geçirilen jSON ve [$errorsFilename] dosyalarında karşılaşılan hatalar kaydedilir (9. satır);
- 41. satır: Oluşturulan [TaxPayerData] nesnelerinin dizisi döndürülür: bu, yöntemin amacıydı;
- satır 44-83: özel yöntem [check], 44. satırda parametre olarak geçirilen [$attributesOfTaxPayerData] dizisinin [marié, enfants, salaire] parametrelerinin geçerliliğini kontrol eder. Hatalı öznitelikler varsa, bunları [$attributes] tablosunda (satır 47, 53, 60, 70) bir [‘attribut erroné’=> valeur de l’attribut erroné] tablosu şeklinde biriktirir;
- 78. satır: Hata varsa, [‘erreurs’=>$attributes] tablosu döndürülür;
- satır 81: hata yoksa, boş bir hata tablosu döndürülür;
- satır 86-93: [InterfaceDao] arayüzünün [saveResults] yönteminin uygulanması;
- satır 90: 86. satırda parametre olarak geçirilen jSON [$resultsFilename] dosyasına kaydedilecek jSON dizesi oluşturulur. jSON dizesi bir diziden oluşturulmalıdır:
- dizinin her bir elemanı, bir virgül ve bir satır boşluğu ile bir sonraki elemandan ayrılır;
- tablonun tamamı köşeli parantezler [] ile çevrelenmiştir;
- 92. satır: jSON dizesi, jSON [$resultsFilename] dosyasına kaydedilir;
13.4.3.2. [DaoImpotsWithTaxAdminDataInDatabase] sınıfı
[DaoImpotsWithTaxAdminDataInDatabase] sınıfı, [InterfaceDao] arayüzünü şu şekilde uygular:
<?php
// ad alanı
namespace Application;
// bir sınıfın tanımlanması ImpotsWithDataInDatabase
class DaoImpotsWithTaxAdminDataInDatabase implements InterfaceDao {
// bir özelliğin kullanımı
use TraitDao;
// vergi dilimlerine ait verileri içeren TaxAdminData türündeki nesne
private $taxAdminData;
// [Database] türündeki nesne, BD'in özelliklerini içerir
private $database;
// üretici
public function __construct(string $databaseFilename) {
// veritabanının jSON yapılandırması kaydedilir
$this->database = (new Database())->setFromJsonFile($databaseFilename);
// özelliği hazırlanıyor
$this->taxAdminData = new TaxAdminData();
try {
// veritabanına bağlantı açılıyor
$connexion = new \PDO(
$this->database->getDsn(),
$this->database->getId(),
$this->database->getPwd());
// SGBD'te her hata durumunda bir istisna atılmasını istiyoruz
$connexion->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
// bir işlem başlatıyoruz
$connexion->beginTransaction();
// vergi dilimleri tablosu doldurulur
$this->getTranches($connexion);
// sabitler tablosu doldurulur
$this->getConstantes($connexion);
// işlem başarıyla sonlandırılır
$connexion->commit();
} catch (\PDOException $ex) {
// devam eden bir işlem var mı?
if (isset($connexion) && $connexion->inTransaction()) {
// işlem başarısızlıkla sonlandırılıyor
$connexion->rollBack();
}
// istisna, çağıran koda iletilir
throw new ExceptionImpots($ex->getMessage());
} finally {
// bağlantı kapatılıyor
$connexion = NULL;
}
}
// veritabanından veriler okunuyor
private function getTranches($connexion): void {
…
}
// sabitler tablosu okunuyor
private function getConstantes($connexion): void {
…
}
// vergi hesaplamasına olanak tanıyan verileri döndürür
public function getTaxAdminData(): TaxAdminData {
return $this->taxAdminData;
}
}
Açıklamalar
- 4. satır: [dao] katmanının diğer uygulamaları için halihazırda kullanılan ad alanı korunur;
- 7. satır: [DaoImpotsWithTaxAdminDataInDatabase] sınıfı, [InterfaceDao] arayüzünü uygular;
- 9. satır: [TraitDao] özelliğini içe aktarıyoruz. Bu özelliğin arayüzün bir kısmını uyguladığı biliniyor. Geriye kalan tek yöntem, 62-64. satırlardaki [getTaxAdminData] yöntemidir. Bu yöntem, yalnızca 11. satırdaki [taxAdminData] özel özniteliğini döndürür. Bundan, oluşturucunun bu özniteliği başlatması gerektiği sonucuna varılır. Bu, oluşturucunun tek görevidir;
- 16. satır: yapıcı, tek parametre olarak [$databaseFilename] değerini alır; bu, jSON [database.json] adlı dosyayı tanımlar ve bu dosya da MySQL [dbimpots-2019] veritabanını tanımlar ;
- 18. satır: jSON ve [$databaseFilename] dosyaları, 13. satırdaki [$database] özniteliğinde oluşturulup depolanan [Database] türünde bir nesne oluşturmak için kullanılır. jSON dosyası düzgün bir şekilde işlenemediğinde, bir [ExceptionImpots] istisnası atılmıştır;
- 20. satır: Oluşturucunun başlatması gereken [$this→taxAdminData] nesnesi oluşturulur;
- 22-26. satırlar: Veritabanına bağlantı kurulur. [PDO] sınıfını belirtmek için PHP yerine [\PDO] notasyonunun kullanıldığına dikkat edin. Nitekim, [Application] ad alanında olduğumuz için, sadece [PDO] yazarsak, bu göreceli ada geçerli ad alanı öne eklenir ve dolayısıyla mevcut olmayan [Application\PDO] sınıfı ortaya çıkar;
- 28. satır: Bir hata oluştuğunda, SGBD, bir \PDOException başlatır (37. satır);
- satır 30: Bir işlem başlatılır. Bu işlem pek de yararlı değildir, çünkü yalnızca iki SQL komutu yürütülecektir ve bu komutlar veritabanını değiştirmez. Yine de bunu, veritabanının diğer kullanıcılarından izole olmak için yapıyoruz;
- satır 32: [tbtranches] vergi dilimleri tablosunun okunması, 52. satırdaki özel yöntem [getTranches] ile gerçekleştirilir;
- satır 34: [tbconstantes] hesaplama sabitleri tablosunun okunması, satır 57'deki özel yöntem [getConstantes] ile gerçekleştirilir;
- satır 36: bu satıra gelindiğinde her şey yolunda gitmiş demektir. Dolayısıyla işlem onaylanır;
- 37-42. satırlar: buraya gelindiğinde bir istisna meydana gelmiştir. Dolayısıyla, devam eden bir işlem varsa (satır 39-42) işlemi geçersiz kılıyoruz. Satır 44: istisnaların tutarlı olması için, alınan istisna mesajını bu sefer [ExceptionImpots] türünde bir istisna olarak yeniden gönderiyoruz;
- satır 45-48: her durumda (istisna olsun ya da olmasın) bağlantıyı kapatırız;
[getTranches] yöntemi şu şekildedir:
private function getTranches($connexion): void {
// kısayollar
$database = $this->database;
$taxAdminData = $this->taxAdminData;
// sorgu hazırlanır SELECT
$statement = $connexion->prepare(
"select {$database->getColLimites()}," .
" {$database->getColCoeffR()}," .
" {$database->getColCoeffN()}" .
" from {$database->getTableTranches()}");
// hazırlanan komutu vergi dilimlerinin değerleriyle birlikte yürütülür
$statement->execute();
// sonuçların değerlendirilmesi
$limites = [];
$coeffR = [];
$coeffN = [];
// üç tablonun doldurulması
while ($tranche = $statement->fetch(\PDO::FETCH_OBJ)) {
$limites[] = (float) $tranche->{$database->getColLimites()};
$coeffR[] = (float) $tranche->{$database->getColCoeffR()};
$coeffN[] = (float) $tranche->{$database->getColCoeffN()};
}
// veriler, sınıfın [$taxAdminData] özniteliğinde saklanır
$taxAdminData->setFromArrayOfAttributes([
"limites" => $limites,
"coeffR" => $coeffR,
"coeffN" => $coeffN
]);
}
Açıklamalar
- 1. satır: Yöntem, açık bir bağlantı olan ve içinde bir işlem devam eden [$connexion] parametresini alır;
- 2-4. satırlar: Kodun her yerine [$this->database] ve [$taxAdminData = $this->taxAdminData] yazmak zorunda kalmamak için iki kısayol oluşturulur. Burada nesnelerin kendilerinin kopyaları değil, nesne referanslarının kopyaları bulunur;
- satır 6-10: SELECT komutu hazırlanır ve 12. satırda yürütülür;
- 13-22. satırlar: SELECT'in sonucu işlenir. Alınan bilgiler üç [limites, coeffR, coeffN] dizisinde biriktirilir;
- 24-28. satırlar: Üç dizi, sınıfın [$this->taxAdminData] özniteliğini başlatmak için kullanılır;
[getConstantes] özel yöntemi şu şekildedir:
private function getConstantes($connexion): void {
// kısayollar
$database = $this->database;
$taxAdminData = $this->taxAdminData;
// SELECT sorgusu hazırlanıyor
$select = "select {$database->getColPlafondQfDemiPart()}," .
"{$database->getColPlafondRevenusCelibatairePourReduction()}," .
"{$database->getColPlafondRevenusCouplePourReduction()}," . "{$database->getColValeurReducDemiPart()}," .
"{$database->getColPlafondDecoteCelibataire()}," . "{$database->getColPlafondDecoteCouple()}," .
"{$database->getColPlafondImpotCelibatairePourDecote()}," . "{$database->getColPlafondImpotCouplePourDecote()}," .
"{$database->getColAbattementDixPourcentMax()}," . "{$database->getColAbattementDixPourcentMin()}" .
" from {$database->getTableConstantes()}";
$statement = $connexion->prepare($select);
// hazırlanan komut çalıştırılır
$statement->execute();
// sonuçtan yararlanılır - burada sadece 1 satır var
$row = $statement->fetch(\PDO::FETCH_OBJ);
// [$taxAdminData] özniteliği başlatılıyor
$taxAdminData->setPlafondQfDemiPart($row->{$database->getColPlafondQfDemiPart()});
$taxAdminData->setPlafondRevenusCelibatairePourReduction(
$row->{$database->getColPlafondRevenusCelibatairePourReduction()});
$taxAdminData->setPlafondRevenusCouplePourReduction($row->{$database->getColPlafondRevenusCouplePourReduction()});
$taxAdminData->setValeurReducDemiPart($row->{$database->getColValeurReducDemiPart()});
$taxAdminData->setPlafondDecoteCelibataire($row->{$database->getColPlafondDecoteCelibataire()});
$taxAdminData->setPlafondDecoteCouple($row->{$database->getColPlafondDecoteCouple()});
$taxAdminData->setPlafondImpotCelibatairePourDecote($row->{$database->getColPlafondImpotCelibatairePourDecote()});
$taxAdminData->setPlafondImpotCouplePourDecote($row->{$database->getColPlafondImpotCouplePourDecote()});
$taxAdminData->setAbattementDixPourcentMax($row->{$database->getColAbattementDixPourcentMax()});
$taxAdminData->setAbattementDixPourcentMin($row->{$database->getColAbattementDixPourcentMin()});
}
Açıklamalar
- 1. satır: Yöntem, açık bir bağlantı olan ve içinde bir işlem devam eden [$connexion] parametresini alır;
- 2-4. satırlar: Kodun her yerine [$this->database] ve [$taxAdminData = $this->taxAdminData] yazmak zorunda kalmamak için iki kısayol oluşturulur. Burada nesnelerin kendilerinin kopyaları değil, nesne referanslarının kopyaları bulunur;
- satır 6-15: SELECT komutu hazırlanır ve 15. satırda yürütülür;
- 17-29. satırlar: SELECT'in sonucu işlenir. Elde edilen bilgiler, sınıfın [$this->taxAdminData] özniteliğini başlatmak için kullanılır;
Not: Sınıfın SGBD ve MySQL'e bağlı olmadığına dikkat edilmelidir. Veritabanındaki DSN aracılığıyla kullanılan SGBD'i belirleyen, çağıran koddur.
13.4.4. [métier] katmanı

- az önce [dao] katmanını (3) uyguladık;
- [InterfaceDao] arayüzüne uyduğumuz için, [métier] katmanı (2) teorik olarak değişmeden kalabilir. Ancak, sadece [dao] katmanını değiştirmedik. Tüm katmanlar tarafından paylaşılan varlıkları da değiştirdik;
[métier] katmanı, aşağıdaki [InterfaceMetier] arayüzünü uygular:
<?php
// ad alanı
namespace Application;
interface InterfaceMetier {
// bir vergi mükellefinin vergilerinin hesaplanması
public function calculerImpot(string $marié, int $enfants, int $salaire): array;
// toplu iş modunda vergi hesaplaması
public function executeBatchImpots(string $taxPayersFileName, string $resultsFileName, string $errorsFileName): void;
}
- 12. satır: [executeBatchImpots] yöntemi artık jSON [$taxPayersFileName] dosyasını kullanmaktadır; oysa 04 sürümünde bu, basit bir metin dosyasıydı. ;
04 sürümünde, [executeBatchImpots] yöntemi şu şekildeydi:
public function executeBatchImpots(string $taxPayersFileName, string $resultsFileName, string $errorsFileName): void {
// [dao] katmanından gelen istisnaların yukarıya iletilmesine izin verilir
// vergi mükellefi verilerinin alınması
$taxPayersData = $this->dao->getTaxPayersData($taxPayersFileName, $errorsFileName);
// sonuç tablosu
$results = [];
// veriler işlenir
foreach ($taxPayersData as $taxPayerData) {
// vergi hesaplanıyor
$result = $this->calculerImpot(
$taxPayerData->getMarié(),
$taxPayerData->getEnfants(),
$taxPayerData->getSalaire());
// formu doldurulur [$taxPayerData]
$taxPayerData->setMontant($result["impôt"]);
$taxPayerData->setDécôte($result["décôte"]);
$taxPayerData->setSurCôte($result["surcôte"]);
$taxPayerData->setTaux($result["taux"]);
$taxPayerData->setRéduction($result["réduction"]);
// sonuç sonuç tablosuna ekleniyor
$results [] = $taxPayerData;
}
// sonuçların kaydedilmesi
$this->dao->saveResults($resultsFileName, $results);
}
- 15. satır artık hatalıdır. [TaxPayerData] sınıfının yeni tanımında, [setMontant] yöntemi artık mevcut değildir;
05 sürümünde, [executeBatchImpots] yöntemi şu şekilde olacaktır:
public function executeBatchImpots(string $taxPayersFileName, string $resultsFileName, string $errorsFileName): void {
// [dao] katmanından gelen istisnaların yukarıya aktarılmasına izin verilir
// vergi mükellefi verilerini alın
$taxPayersData = $this->dao->getTaxPayersData($taxPayersFileName, $errorsFileName);
// sonuç tablosu
$results = [];
// veriler üzerinde işlem yapılır
foreach ($taxPayersData as $taxPayerData) {
// vergi hesaplanıyor
$result = $this->calculerImpot(
$taxPayerData->getMarié(),
$taxPayerData->getEnfants(),
$taxPayerData->getSalaire());
// formu doldurulur [$taxPayerData]
$taxPayerData->setFromArrayOfAttributes($result);
// sonuç sonuç tablosuna ekleniyor
$results [] = $taxPayerData;
}
// sonuçların kaydedilmesi
$this->dao->saveResults($resultsFileName, $results);
}
Açıklamalar
- 15. satır: [TaxPayerData] sınıfının tek tek setter'larını kullanmak yerine, global setter'ı [setFromArrayOfAttributes] kullanılır;
- kodun geri kalan kısmında herhangi bir değişiklik yapılmasına gerek yoktur;
13.4.5. Ana komut dosyası

- az önce [dao] (3) ve [métier] (2) katmanlarını uyguladık;
- geriye ana komut dosyasını (1) yazmak kaldı;
Ana komut dosyası, 04 sürümündekine benzerdir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı sıkıya uyulur
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__ . "/../Entities/BaseEntity.php";
require_once __DIR__ . "/../Entities/TaxAdminData.php";
require_once __DIR__ . "/../Entities/TaxPayerData.php";
require_once __DIR__ . "/../Entities/Database.php";
require_once __DIR__ . "/../Entities/ExceptionImpots.php";
require_once __DIR__ . "/../Utilities/Utilitaires.php";
require_once __DIR__ . "/../Dao/InterfaceDao.php";
require_once __DIR__ . "/../Dao/TraitDao.php";
require_once __DIR__ . "/../Dao/DaoImpotsWithTaxAdminDataInDatabase.php";
require_once __DIR__ . "/../Métier/InterfaceMetier.php";
require_once __DIR__ . "/../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 hesaplama
$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
- 12-22. satırlar: sürüm 05'teki tüm dosyaların yüklenmesi;
- 25-29. satırlar: uygulamanın çeşitli jSON dosyalarının adları;
- 33. satır: [dao] katmanının oluşturulması;
- satır 35: [métier] katmanının oluşturulması;
- satır 37: [métier] katmanının [executeBatchImpots] yönteminin çağrılması;
Sonuçlar
Uygulama iki adet jSON dosyası üretir:
- [resultats.json]: çeşitli vergi hesaplamalarının sonuçları;
- [errors.json]: jSON ve [taxpayersdata.json] dosyalarında bulunan hataları bildirir;
[errors.json] dosyası şöyledir:
{
"numéro": 1,
"erreurs": [
{
"marié": "ouix"
},
{
"enfants": "2x"
},
{
"salaire": "55555x"
}
]
}
Bu, [taxpayersdata.json] dosyasında, vergi mükellefleri tablosunun 1 numaralı öğesinde hata olduğu anlamına gelir. [taxpayersdata.json] dosyası şöyleydi:
[
{
"marié": "oui",
"enfants": 2,
"salaire": 55555
},
{
"marié": "ouix",
"enfants": "2x",
"salaire": "55555x"
},
{
"marié": "oui",
"enfants": "2",
"salaire": 50000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 50000
},
{
"marié": "non",
"enfants": 2,
"salaire": 100000
},
{
"marié": "non",
"enfants": 3,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 5,
"salaire": 100000
},
{
"marié": "non",
"enfants": 0,
"salaire": 100000
},
{
"marié": "oui",
"enfants": 2,
"salaire": 30000
},
{
"marié": "non",
"enfants": 0,
"salaire": 200000
},
{
"marié": "oui",
"enfants": 3,
"salaire": 20000
}
]
Sonuç dosyası [resultats.json] ise şu şekildedir:
[
{
"marié": "oui",
"enfants": 2,
"salaire": 55555,
"impôt": 2814,
"surcôte": 0,
"décôte": 0,
"réduction": 0,
"taux": 0.14
},
{
"marié": "oui",
"enfants": "2",
"salaire": 50000,
"impôt": 1384,
"surcôte": 0,
"décôte": 384,
"réduction": 347,
"taux": 0.14
},
{
"marié": "oui",
"enfants": 3,
"salaire": 50000,
"impôt": 0,
"surcôte": 0,
"décôte": 720,
"réduction": 0,
"taux": 0.14
},
{
"marié": "non",
"enfants": 2,
"salaire": 100000,
"impôt": 19884,
"surcôte": 4480,
"décôte": 0,
"réduction": 0,
"taux": 0.41
},
{
"marié": "non",
"enfants": 3,
"salaire": 100000,
"impôt": 16782,
"surcôte": 7176,
"décôte": 0,
"réduction": 0,
"taux": 0.41
},
{
"marié": "oui",
"enfants": 3,
"salaire": 100000,
"impôt": 9200,
"surcôte": 2180,
"décôte": 0,
"réduction": 0,
"taux": 0.3
},
{
"marié": "oui",
"enfants": 5,
"salaire": 100000,
"impôt": 4230,
"surcôte": 0,
"décôte": 0,
"réduction": 0,
"taux": 0.14
},
{
"marié": "non",
"enfants": 0,
"salaire": 100000,
"impôt": 22986,
"surcôte": 0,
"décôte": 0,
"réduction": 0,
"taux": 0.41
},
{
"marié": "oui",
"enfants": 2,
"salaire": 30000,
"impôt": 0,
"surcôte": 0,
"décôte": 0,
"réduction": 0,
"taux": 0
},
{
"marié": "non",
"enfants": 0,
"salaire": 200000,
"impôt": 64210,
"surcôte": 7498,
"décôte": 0,
"réduction": 0,
"taux": 0.45
},
{
"marié": "oui",
"enfants": 3,
"salaire": 20000,
"impôt": 0,
"surcôte": 0,
"décôte": 0,
"réduction": 0,
"taux": 0
}
]
Bu sonuçlar, 04 sürümündeki sonuçlarla uyumludur.
13.5. [Codeception] Testleri
Sürüm 04 ile ilgili bölümde yapıldığı gibi, sürüm 05 için de [Codeception] testleri yazacağız.

13.5.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-05");
define("VENDOR", "C:/myprograms/laragon-lite/www/vendor");
// arayüz ve sınıfların dahil edilmesi
require_once ROOT . "/Entities/BaseEntity.php";
require_once ROOT . "/Entities/TaxAdminData.php";
require_once ROOT . "/Entities/TaxPayerData.php";
require_once ROOT . "/Entities/Database.php";
require_once ROOT . "/Entities/ExceptionImpots.php";
require_once ROOT . "/Utilities/Utilitaires.php";
require_once ROOT . "/Dao/InterfaceDao.php";
require_once ROOT . "/Dao/TraitDao.php";
require_once ROOT . "/Dao/DaoImpotsWithTaxAdminDataInDatabase.php";
require_once ROOT . "/Métier/InterfaceMetier.php";
require_once ROOT . "/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";
const TAXADMINDATA_FILENAME = ROOT ."/Data/taxadmindata.json";
const RESULTS_FILENAME = ROOT ."/Data/resultats.json";
const ERRORS_FILENAME = ROOT ."/Data/errors.json";
const TAXPAYERSDATA_FILENAME = ROOT ."/Data/taxpayersdata.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() {
// hesaplama sabitleri
$this->assertEquals(1551, $this->taxAdminData->getPlafondQfDemiPart());
…
}
}
Açıklamalar
- 9-33. satırlar: test ortamının tanımlanması. Bağlantı paragrafında açıklanan ana komut dosyası [MainCalculateImpotsWithTaxAdminDataInMySQLDatabase] ile aynı ortamı kullanıyoruz;
- 39-44. satırlar: [dao] katmanının oluşturulması;
- 43. satır: [$this→taxAdminData] özniteliği, test edilecek verileri içerir;
- 47-51. satırlar: [testTaxAdminData] yöntemi, “bağlantı” başlıklı paragrafta açıklanan yöntemdir;
Test sonuçları şu şekildedir:

13.5.2. [métier] katman testi
[MetierTest.php] testi şu şekildedir:
<?php
// fonksiyon parametrelerinin beyan edilen türlerine sıkı uyum
declare (strict_types=1);
// ad alanı
namespace Application;
// kök dizinler
define("ROOT", "C:/Data/st-2019/dev/php7/poly/scripts-console/impots/version-05");
define("VENDOR", "C:/myprograms/laragon-lite/www/vendor");
// arayüz ve sınıfların dahil edilmesi
require_once ROOT . "/Entities/BaseEntity.php";
require_once ROOT . "/Entities/TaxAdminData.php";
require_once ROOT . "/Entities/TaxPayerData.php";
require_once ROOT . "/Entities/Database.php";
require_once ROOT . "/Entities/ExceptionImpots.php";
require_once ROOT . "/Utilities/Utilitaires.php";
require_once ROOT . "/Dao/InterfaceDao.php";
require_once ROOT . "/Dao/TraitDao.php";
require_once ROOT . "/Dao/DaoImpotsWithTaxAdminDataInDatabase.php";
require_once ROOT . "/Métier/InterfaceMetier.php";
require_once ROOT . "/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";
const TAXADMINDATA_FILENAME = ROOT ."/Data/taxadmindata.json";
const RESULTS_FILENAME = ROOT ."/Data/resultats.json";
const ERRORS_FILENAME = ROOT ."/Data/errors.json";
const TAXPAYERSDATA_FILENAME = ROOT ."/Data/taxpayersdata.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() {
$result = $this->métier->calculerImpot("oui", 2, 55555);
$this->assertEqualsWithDelta(2815, $result["impôt"], 1);
$this->assertEqualsWithDelta(0, $result["surcôte"], 1);
$this->assertEqualsWithDelta(0, $result["décôte"], 1);
$this->assertEqualsWithDelta(0, $result["réduction"], 1);
$this->assertEquals(0.14, $result["taux"]);
}
…………………………………………………………………………………………………………………..
public function test11() {
$result = $this->métier->calculerImpot("oui", 3, 200000);
$this->assertEqualsWithDelta(42842, $result["impôt"], 1);
$this->assertEqualsWithDelta(17283, $result["surcôte"], 1);
$this->assertEqualsWithDelta(0, $result["décôte"], 1);
$this->assertEqualsWithDelta(0, $result["réduction"], 1);
$this->assertEquals(0.41, $result["taux"]);
}
}
Açıklamalar
- 9-33. satırlar: test ortamının tanımlanması. Bağlantı paragrafında açıklanan ana komut dosyası [MainCalculateImpotsWithTaxAdminDataInMySQLDatabase] ile aynı ortamı kullanıyoruz;
- 39-45. satırlar: [dao] ve [métier] katmanlarının oluşturulması;
- 44. satır: [$this→métier] özniteliği, [métier] katmanına atıfta bulunur;
- satır 47-64: [test1, test2…, test11] yöntemleri, bağlantı paragrafında açıklanan yöntemlerdir;
Test sonuçları şöyledir:
