Dependency Injection

teroristd

Registered
Здравейте, имам следната ситуация. В един клас държа разни масиви. В примера ще са само два но иначе ще са повече. В друг клас, искам да инжектирам тези масиви(може и един голям масив да ги държи) и в трети клас искам да получа по една стойност от тях. Един вид в третия клас да кажа икам от масив $а стойност 1, после някъде по надолу от масив $b стойност 2. Как трябва да стане?

Код:
class Masivi {
public function arr(){
$a = array('a1', 'a2');
$b = array('b1', 'b2');
}
}
 
Проблема, който описваш няма нищо общо с темина „Dependancy Injection“.Моля те, оправи си заглавието. Дообясни какво точно искаш! Доколкото разбирам, трябва във всеки конструктор на съотвените класове, да подаваш като параметър масива, който искаш и съответно да го „обработваш“ с необходините методи
 
Да доста са объркани нещата сори. В разни примери за DI които разглеждах е показано че имаме например два обекта които извършват съвсем различни работи и трети обект на който работата му зависи от някой от другите два обекта. Ние вместо да пишеме някакви ифове му подаваме обект с който да работи. Аз исках същото но вместо обекти да подавам масиви. Както казваш в крайна сметка тотално съм объркал концепциите.

Искам да имам едно централизирано място където да имам разни масиви със различни стойности и някъде другъде да взема точно определена стойност от някой масив и да си работя с нея. Но искам да е нещо като шорткът. Един вид да не ми се налага да пиша разни форичове и други а директно да извикам един метод и да подам като параметър масива и някакъв ключ и да получа стойността.
 
За да ползваш идеята на DI, то си направи един клас, който е екстрактор на тези масиви. А тези масиви ги прави в отделни файлове или в един, както решиш. Но трябва да си изградиш някаква структура.
От там(щом държиш да е ОО) си направи конструктора примерно да извиква файла(ако е един) или един метод, който взима параметри, примерно първия параметър кой файл да е, или ако е един файл, кой ключ(подмасив) да извика, а втория кой точно ключ от подмасива искаш.

Аз по принцип това го правя с един параметър, в който извикването ми е разделител с точка ( array.key1.key2 ). Така имам всякаква дълбочина.

Ако искаш погледни как съм го направил в този урок ( функции getConfig или getLanguageText). http://web-tourist.net/login/login/view.php?st=3460

И вече в конструктора, в който искаш да го вкараш му извикваш този клас.
 
Благодаря за отговора. DI така и не можах да го използвам. Нещо не мога да го разбера правилно явно ми трябва още четене :) . Ето как го направих, може на някой да му е полезно. Правя един клас, нещо като registry само че с един параметър в сет. После в класа, в който държа данните правя един голям масив, който държи всички останали и го подавам на сет метода и си викам гет метода. Моделите го наследяват, а в контролера вече си взимам каквото ми трябва. Идеята е че във вю-тата имам само един хедър едно боди и един футър но им предоставям различни данни от различните контролери. Може и без да задаваш имена на ключовете в масивите и да си ги викаш просто [0][1]. Ето как става.

Код:
namespace app;

class Registry {

    private static $_data = array();

    private function __construct() {
        
    }

    public static function setData($data) {
        self::$_data = $data;
    }

    public static function getData() {
        return self::$_data;
    }

}


namespace app;

class Container {

    protected $_registrySet;
    protected $_registryGet;
    protected $_masterArr;

    protected function __construct() {

        $this->_masterArr = array(
        'titleArr' => array('log'=>'login', 'reg'=>'register'),
        'descriptionArr' => array('Description', 'Other Description'),
        'keywordsArr' => array('Furst Page Keywords', 'Second Page Keywords'));
        
        $this->_registrySet = Registry::setData($this->_masterArr);
        $this->_registryGet = Registry::getData();
    }

}


namespace models;

class ProbaModel extends \app\Container {

    public function __construct() {
        parent::__construct();
    }

    public function title() {
       
            return $this->_registryGet['titleArr']['reg'];
        
    }

}


namespace controllers;

class IndexProba {

    public function proba() {
        $model = new \models\ProbaModel();
        echo $model->title();
    }

}
 
По този пример се вижда, че не разбираш цялата концепция на MVC. Аз виждам джаста-праста код. Обърни сериозно внимание на шаблоните, на какви категории се разделят, кой в каква категория попада и за какво се ползва. Тука един не видях да се ползва по предназначение. Тези големи масиви в конструктора също не ми харесват. Не знам от къде четеш за ооп, шаблони и мвц, но май трябва да си смениш източника.

Съжалявам, че съм критичен, но просто не ми допада идеята ти на реализация.
 
Лошо няма да си критичен, не се сърдя. Човек се учи от грешките си. Ако говориш за това че съм дал echo в контролера, то е само за пробата иначе си викам вю-та. За масивите в конструктора, не виждам нищо лошо да са там. Може да ги изкарам в някакъв друг метод, но при положение че ще са ми необходими навсякъде не виждам смисъл да минавам през още един метод за да ги достъпя. Ако може дай код как би го направил ти.
 
teroristd каза:
Лошо няма да си критичен, не се сърдя. Човек се учи от грешките си. Ако говориш за това че съм дал echo в контролера, то е само за пробата иначе си викам вю-та. За масивите в конструктора, не виждам нищо лошо да са там. Може да ги изкарам в някакъв друг метод, но при положение че ще са ми необходими навсякъде не виждам смисъл да минавам през още един метод за да ги достъпя. Ако може дай код как би го направил ти.

Ами ти бягаш по този начин от ООП.

Идеята на този модел каква е? Щом си дал такъв пример, значи ще изпраскаш някаква глупост. Идеята на модела е да работи с данните. Дали ще правиш връзка с база данни, ще четеш и пишеш файлове и тем подобните му, то това е работа на модела. Този контейнер, който викаш не е правилен.

По-добре махни разширяването и използвай DI, като в конструктора на модела направиш инстанция към този контейнер. Лично аз бих направил, както ти казах по-горе. Правя си клас, който работи с масиви. Чете, пише, променя, създава, трие и т.н.
Когато направиш този клас, то той ще ти е като библиотека, която ще можеш да си ползваш, когато искаш без да се налага да разширяваш клас с нея, защото някъде ще ти се наложи да разшириш с по-важен клас и това вече няма да бъде използваемо.
Вместо това, в конструктора си извикваш инстанция на този клас. Имаш си метод примерно load, с който ще заредиш някой масив.(масива може да е файл, който директно връща масива)
След като load-неш този масив, присвояваш го и си работиш с него.
Така можеш да направиш във всеки модел. Но идеята, която ти явно искаш да направиш, не е за модел. По-скоро ще ти трябва в контролера, за да подаваш нещо към вю-то.

Сега не мога да изпадам в подробности, защото нямам време. Но, ако искаш пиши на ЛС, по-късно или утре ще доизясня нещата.
 
Благодаря. Все пак се надявам да дадеш код когато имаш време за да се ориентирам по точно как е добре да станат нещата.
 
teroristd каза:
Благодаря. Все пак се надявам да дадеш код когато имаш време за да се ориентирам по точно как е добре да станат нещата.

Ами ще гледам тези дни да драсна нещо примерно, че ми е много натоварен този месец.
 
Ще ти покажа два начина, както аз бих го направил.

Набързо начатках един екстрактор. Има неща, които не са изпипани, но толкова като за пример. Няма да изпадам в подробности с модели, контролери и т.н., защото няма особено значение.

Ще давам линкове към pastebin, за да не става много дълъг поста.


Първия вариант е използването на Traits

Този вариант е добър като начин на ползване, но не е особено практически добър.

ArrayExtractor
array1.php

Поради простата причина, че може да се ползва навсякъде и че е Trait, се наложи да слагам prefix на методите, но това не е толкова лошо, ако се ползва като библиотека.

Начина, по който се ползва.

PHP:
class Test {
	use ArrayExtractor;
	
	public function __construct() {
		
		$this->aeSetPath('./files')
				->aeSetClearFlag(false)
				->aeLoad('array1.php', 'a1')
				->aeLoad('array2.php', 'a2');
	}
	
	public function PartlyExtracted() {		
		// така екстрактваме само това, което ни трябва
		
		// $this->aeExtract(['title', 'subtitle.toString'], 'a1');
		$this->aeExtract(['title' => 'title', 'subtitle' => 'subtitle'], 'a1');
		
		// return $this->aeGet(0, 'a1'); // 0, 1, 2, ...
		return $this->aeGet('subtitle.toString', 'a1');
	}
	
	public function FullyExtracted() {
		// така екстрактваме всичко
		$this->aeExtractAll('a2');
		
		
		return $this->aeGet('title', 'a2');
	}
}

$test = new Test();
var_dump($test->PartlyExtracted());
var_dump($test->FullyExtracted());

Като цяло съм позволил зареждането на няколко файла наведнъж, като им се задават алиаси. След това, като се екстрактва, просто трябва да зададем алиаса на желания файл. Ако викаме само един файл, няма нужда от алиаси. Но зададе ли се на aeLoad() алиас, после трябва и на другите методи да се зададе.


Втория начин е със създаването на инстанция на класа, в класа, в който искаме да използваме екстрактора.
За този вариант може да се промени малко класа.

Едно от предимствата на втория вариант е, че можем да задаваме различни инстанции. Това ще позволи задаването на различни дестинации на файловете, както и използването на класа в определени методи, без да бъде зависим от останалите.
Докато в първия не можем да го използваме по този начин. Там се работи с една инстанция и това е инстанцията на класа, в който сме извикали трейта. За това и са добавени алиасите, за да могат да се разграничават масивите, ако се ползват няколко файла.

Сигурно има и още 20 варианта за използването, но аз бих ползвал някой от тези. Разбира се, зависи от ситуацията и колко сложна ще стане йерархията на обектите.

Може би се отплеснах доста от идеята, която имах предвид, но това ми дойде изневиделица.

Нямам достатъчно време да се занимавам по твоя въпрос, за това се извинявам за набързо дадените примери. Надявам се да ти се опресни малко идеята и току-виж ти изникне твоя.
Но обърни силно внимание на MVC и шаблоните. Ако трябва разгледай някой framework да ти се изяснят нещата.
 
Благодаря за кода, ще продължа да те тормозя :D пък когато имаш време ще отговориш.

В случая има някои неща които не разбирам изобщо, и някои неща които, не ми харесват. Не казвам че не е правилно, но целта ми е да се опорости максимално взимането на стойностите от масивите. Хареса ми идеята за трейтовете, чувал съм за тях но не бях чел за какво се ползват. Сега попрочетох малко и ми изглеждат полезен инструмент.

Така да продължа нататък. Първо не разбрах изобщо какви са тия алиаси за които говориш. Този ClearFlag и TempData също не ми е ясно за какво ги ползваш.

Не ми харесва че трябва да се инклудват файловете, и да им се задава път до папка и име на файла всеки път. Това според мен е работа на аутолоадинга. Друго което не ми харесва е синтаксиса по-който са написани масивите и по-точно квадратните скоби. Сигурно има смисъл да ги пишеш така, но на мен не ми харесва.

Още един път ще кажа, целта е максимално просто да се вземат стойностите от разни масиви, като това не нарушава MVC. Предпочитам масивите да са в клас а не в прости php файлове ако е възможно.

Както е казал Айнщайн "Всеки може да направи нещата сложни, но се иска истинска смелост за да тръгнеш по обратния път".
 
Да, извинявам се, просто много ми се спеше и ме домързя да пиша по-подробно. Сам се оплетох в думите си.

Сега варя ракия :D Довечера ще преразгледам поста ми и ще гледам да обясня по-ясно нещата.
 
teroristd каза:
Не ми харесва че трябва да се инклудват файловете, и да им се задава път до папка и име на файла всеки път. Това според мен е работа на аутолоадинга.
Да това е работа на autoloader-а. Можеш да премахнеш тези методи и да интегрираш с autoloader-a.
teroristd каза:
Друго което не ми харесва е синтаксиса по-който са написани масивите и по-точно квадратните скоби. Сигурно има смисъл да ги пишеш така, но на мен не ми харесва.
Това е просто друг начин за писане на масиви. Можеш да си ползваш array() ако искаш.
teroristd каза:
Още един път ще кажа, целта е максимално просто да се вземат стойностите от разни масиви, като това не нарушава MVC. Предпочитам масивите да са в клас а не в прости php файлове ако е възможно.

Защо ти е да енкапсулираш масиви в обекти?
По-логично е те да са в отделни файлове.

Щом ползваш MVC, може да си надраскаш Model-и за тези масиви.
Все пак това е идеята зад тях - да боравят с данни(бази данни, файлове, масиви).
 
eLite каза:
Защо ти е да енкапсулираш масиви в обекти?
По-логично е те да са в отделни файлове.

Щом ползваш MVC, може да си надраскаш Model-и за тези масиви.
Все пак това е идеята зад тях - да боравят с данни(бази данни, файлове, масиви).

Моделите са класове, които работят с данни. От къде ще взимат тези данни няма значение. Може от база данни, от прост файл или от друг клас е все едно. В случая тези данни трябва да стоят някъде. Не че неможе в обикновен файл, но просто по-удобно ще ми е да работя с тях ако са в един клас. Приеми го като базов модел ако така ти харесва. Отделно вече други модели ще работят с тези масиви. Грубо ще се опитам да покажа как ще ги използвам. Няма да изпадам в подробности за цялата система, но например викам някакви вю-та. Хедъра и футъра винаги ще бъдат едни и същи за цялата система, което значи че няма смисъл за всяка страница да правя нови. Обаче например заглавието, дескрипшъна и т.н. ще са различни. Мога във всеки модел да си правя някакви променливи или масиви в които да задавам стойностите, обаче това е тъпо, защото ако в последствие реша да ги сменя трябва да отворя незнайно колко файла и да ги редактирам. Много по лесно и удобно е да има едно централизирано място което, да държи тези данни и ако пипам някъде да е само там. В тази кутийка мога да си слагам всякакви стрингчета, линкчета, простотийки и всичко ще е в масиви, лесно и бързо за достъп и редакция. Това е.
 
ОФФ: 1ч. пиша поста и накрая защо ли да не натисна CTRL + R, че да си рефрешна страницата.

Този път по-накратко ще го дам. Аман от тъпи ситуации вече. Да сложиш рефреш на CTRL+R, акъли.
_________________________________________

Има безброй начини да направиш нещо, но въпроса е да го направиш по по-добър начин. Не търси винаги лесния начин, а търси начина, който покрива всякакви непредвидени ситуации.
Идеята на MVC е да се структурират нещата по най-добрия начин. Да е четимо и лесно откриваемо за всички, които ще работят с този код.
Плюс това въобще не е практично да изпраскаш един голям масив в метод на клас.
Най-добре си отделяй файловете отделно и си ги зареждай там, където ти трябват.

Така да продължа нататък. Първо не разбрах изобщо какви са тия алиаси за които говориш. Този ClearFlag и TempData също не ми е ясно за какво ги ползваш.

1. Alias - екстрактора съм го направил да можеш да зареждаш няколко файла на веднъж, защото ако решиш да си категоризираш файловете, това ще е от голяма полза. Понеже при зареждане, всичко влиза в един масив TempData, то ни трябва да разграничаваме двата файла, защото ако извикаме някой индекс, който присъства и в двата файла, ще стане фал. Поради тази причина съм направил така, че да можеш да си именуваш файловете(масивите).

Пример:
Да речем, че зареждаме два файла, единия е array1.php, другия array2.php. Това всичко влиза в TempData, но ние не искаме това. Като викам някой индекс, искам да знам от кой файл го викам. За това задаваме име. Да речем, че първия файл ще е а1, а втория а2. В случая в TempData се получава следното:

PHP:
$tempData = array(
	'a1' => array(), // масива от първия файл
	'a2' => array() // масива от втория файл
);

По този начин вече знам къде ми е масива от първия файл и къде от втория.
Когато съм задал имена, то после на екстрактора мога да му задам кои индекси от кой масив искам да изкарам.

Пример 2
PHP:
class Test {
	// тука да речем, че сме заредили файловете в конструктора

	public function page1_title() {
		$this->aeExtract(['title' => 'page1.title'], 'a1');
	}

	public function page2_title() {
		$this->aeExtract(['title' => 'page2.title'], 'a2');
	}
}

След това спокойно мога да си викам title, без да се притеснявам, че ще извикам грешния. Просто на aeGet() , ще задам от кой файл искам да взема title.

PHP:
$this->aeGet('title', 'a1'); // това ще ми върне page1.title, който се намира в първия файл
$this->aeGet('title', 'a2'); // това ще ми върне page2.title, който се намира във втория файл

Когато екстрактваш, не е нужно да задаваш име на индекса, но съм го сложил като улеснение, защото ако не зададеш име, ще стане по този начин

PHP:
$this->aeGet(0,  'a1');
// ... тука същото, но с a2

В момента се сетих и за по-добър вариант на направа, но както казах, това е набързо написан екстрактор, за примера, и не съм влагал много-много мисъл, за да е нещо читаво.

Когато извикваш само един файл, не е нужно да задаваш alias име. Тогава просто не подаваш втория параметър и файла си се вкарва във временния масив и после си използваш останалите функции без да посочваш от кой файл искаш да извикаш(защото така или иначе е само един).

Надявам се да схвана идеята на alias имената.


2. TempData - използвам временен масив понеже той винаги се пълни с всички индекси. Но аз, например, не искам винаги да извиквам индекси от целия масив. Поради тази причина, когато екстрактвам взимам само нужните ми индекси и ги вкарвам в основния масив, от който след това извличам информацията. Това съм го направил с цел по-бързо търсене в масива. Защото, ако имам масив с 200 индекса, а аз искам в случая само 2, то ще е по-бързо да индексираш при 2 записа, отколкото при 200

3. ClearFlag - това съм го сложил поради следната причина. Този флаг служи, за да мога да определям дали ще екстрактвам на няколко места. Ако е включен, то екстракта ще ми работи само веднъж, след това временния масив ще се изтрие и няма да имам данните.
При горния случай примерно, ако имам включен флаг, то ще ми даде грешка при втория екстракт, понеже няма да може да намери TempData. Тогава се случва следното. Първия екстракт си казва "Добре, взех това, което искаше, сега ще изтрия временния масив, защото повече няма да ти трябва". И това е супер. Няма нужда да държим нещо, което няма да ни трябва.
Е, да, но ако искаме да направим по-надолу друг екстракт, то няма да можем, защото същата функция ще каже "Чакай сега, ти взе това, което искаше, не можеш да взимаш повече, защото нямам данните" и ще изпраска грешка.
Когато смяташ, че в един клас ще имаш няколко екстракта, то още в началото задай този флаг като изключен, за да не може да се изтрие временния масив и да можем да го ползваме и след това.

Не ми харесва че трябва да се инклудват файловете, и да им се задава път до папка и име на файла всеки път. Това според мен е работа на аутолоадинга.

Това не е точно така. Не е работа на autoloader-a. Работата на autolaoder-a е, да ти предостави нужния клас, точно там, където ти трябва. Но тука говорим за масиви. Ако имаш няколко файла(обърни внимание, че аз гледам да отразявам различни ситуации), то как ще разбере autoloader-a кой да ти зареди ? Ще трябва да описваш за всеки клас точно кои файлове искаш да се заредят.
Точно поради тази причина съм направил manual loading. В нужния клас, ако трябва и в нужния метод си викаш точно този файл, който ще използваш. Ако ти се наложи да ползваш различни файлове, ще си ги заредиш тези, които ти трябват.
Така или иначе ти работиш с шаблон. Когато пишеш един контролер, ти ще знаеш точно какво ще ти трябва да извлечеш.
По този начин имаш опцията за всеки метод да заредиш различен файл, но имай предвид, че ще трябва да задаваш alias names.

Друго което не ми харесва е синтаксиса по-който са написани масивите и по-точно квадратните скоби. Сигурно има смисъл да ги пишеш така, но на мен не ми харесва.

Това си е личен избор. На мен ми е по-готино да използвам квадратните скоби, понеже в последно време пиша само на JavaScript, а там основния им синтаксис е този. Но ти щом искаш да си използваш стандартния начин за PHP, използвай го. Същата работа е.


Още един път ще кажа, целта е максимално просто да се вземат стойностите от разни масиви, като това не нарушава MVC. Предпочитам масивите да са в клас а не в прости php файлове ако е възможно.

Пак ще кажа. Не търси винаги максимално опростеното. Направи така, че да покриваш различни непредвидени ситуации. И това, което ти замисляш напълно нарушава MVC. Модела не съдържа данните, той прави така, че да си ги набави, за да ги даде на контролера.
Много грешно е да се слагат такива големи масиви в класове. Дори големите frameworks си слагат конфигурационните данни в масиви в различни файлове. За пример можеш да вземеш Laravel. Представи си техните файлове, които покриват всякакви аспекти(app, database, cache, broadcasting, filesystems, mail, session, view, services и т.н.) да са ти в един клас, само и само да е по-удобно за ползване. Еми, аз ако видя или работя с такъв вид структура, то или ще псувам създателя на това нещо или ще се принудя да го преправя, а никой не иска да си губи времето с нещо, което негов колега е трябвало да направи предварително.

Този екстрактор можеш дори да го ползваш за Локализирането на сайта(Multilingual), като си викаш езиковите файлове и си ги екстрактваш така, както си искаш.

Относно Трейтовете, аз бих си останал на този вариант. Трейтовете се ползват, когато имаш голямо повтаряне на код и голям използване. В случая, ако го ползваш постоянно, във всеки контролер, да речем - трейтовете според мен са идеална алтернатива.


П.П. Надявам се, че не съм изтървал да кажа нещо и че този път съм описал нещата по-ясно. :D
 
Да този път е много по добре. Както му се вика почти всичко разбрах :D . Благодаря ти, ще оставя темата отворена, да мога да пиша ако ми изникнат някакви въпроси.
 

Back
Горе