Gå til innhold

Hvordan definere flere klasser mot en samme foreldre klasse?


Anbefalte innlegg

Jeg har en klasse som håndterer database tilkoblingene mine:

class DB
{
    private $con = array();  //  holder en array med alle tilkoblingene

    public function add($alias, $db, $un, $pw, $desc)  //  legger til en ny tilkobling
    {
        try {
            $options = array(/*PDO OPTIONS*/);			
            $this->con[$alias] = array('alias'=>$alias, 'db'=>$db, 'dbh'=>new PDO('mysql:host=localhost;dbname='.$db.';charset=utf8', $un, $pw, $options), 'desc'=>$desc);
        } catch(PDOException $e) {
            /*  returner en feilmelding  */
        }
    }

    public function con($alias=null)  //  ta i bruk tilkoblingen
    {
        if (is_null($alias)) return $this->con;  //  returner en array med alle tilkoblingene
        else return $this->con[$alias]['dbh'];  //  returner PDO objektet til en tilkobling basert på et angitt alias
    }
}

Videre har jeg en annen klasse som tar i bruk denne DB klassen: (eksempel)

class SQL extends DB
{
    public function showTables($alias='',$table_name='')
    {
        if(empty($alias)||$alias===true){  //  empty = vis alle tilgjenglige tabeller fra alle tilgjenglige tilkoblinger, true = viser også feltene
            foreach($this->con() as $con){  //  når con() benyttes uten angitt alias, så returneres et array med alle tilgjenglige tilkoblinger
                $html .= '"'.$con['alias'].'"';  //  viser tilkoblings alias
                $tables = $this->con($con['alias'])->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN);  //  her benyttes con() aktivd da vi her bruker PDO objektet for å koble oss mot databasen
                foreach($tables as $table){
                    $html .= $table;  //  viser tabell navn
                    if($alias===true){  //  viser også feltene i tabellen hvis denne er satt til true
                        $columns = $this->con($con['alias'])->query('SHOW FULL COLUMNS FROM '.$table)->fetchAll();
                        foreach($columns as $column){
                            $html .= $column['Field'].' '.$column['Type'].' '.($column['Comment']!=''?$column['Comment']:'');  //  viser felt navn samt diverse info
                        }
                    }
                }
            }
        } else {  //  viser kun tabeller tilhørende en angitt tilkobling (alias)
            /*  igrunnen samme kode som ovenfor, bare at vi her begrenser resultatet til angitt tilkobling. eller tabell om et tabellnavn også er angitt i funksjonen  */
        }
    }
}

Som dere kan se, så benytter showTables() metoden seg av con() metoden fra foreldre klassen (DB) via $this->con().

 

Jeg må først starte DB klassen, samt definere minst en gyldig tilkobling:

$_SITE['db']['class'] = new SQL();  //  ved å starte barne-klassen, så blir naturligvis metodene i foreldreklassen også tilgjenglig
$_SITE['db']['class'] -> add('alias', 'databasenavn','databasebruker','brukerpassord','valgfri beskrivelse for tilkoblingen');
$_SITE['db']['class'] -> add('alias2', 'databasenavn2','databasebruker2','brukerpassord2','');

Når jeg har gjort dette, så vil begge disse tilkoblingen være tilgjenglige via con(). Dvs. at SQL nå vil kunne benytte seg av $this->con('alias') eller $this->con('alias2') for å kjøre spørringer mot databasen.

 

Men det som er greia, eller problemstillingen min, nå er at jeg trenger nå å gjøre denne tilkoblingen tilgjenglig i flere klasser som.

 

Hvordan gjør jeg det?

 

For at PDO objektet skal være tilgjenglig i barne-klassen, så må add() metoden defineres i samme variabel. Dvs. at jeg tydligvis ikke kan gjøre dette:

/**
 *  opprette en ny klasse slik
 */
class query extends DB
{
    public function find($alias,$columns,$table,$where)
    {
        $qry = $this->con($alias)->query('SELECT '.$columns.' FROM '.$table.' WHERE '.$where)->fetchAll();
        return $qry;
    }
}

/**
 *  og benytte den slik
 */
$db = new query();
$db->find('alias', 'name', 'table','id=1');

Selv om denne klassen også kobler seg til DB klassen, så er tydligvis ikke PDO tilkoblingen tilgjenglig av den grunn. Fordi $db og $_SITE['db']['class'] ikke inneholder samme verdier mest sannsynligvis.

 

Så spørsmålet mitt er: Hvordan kan jeg gjøre databasetilkoblingen min tilgjenglig i "alle" klasser som trenger den. Uten bruk av statiske metoder etc.

 

Takker på forhånd for alle forslag..

Lenke til kommentar
Videoannonse
Annonse

Måten du bruker extend funksjonalitet her en feil, du burde enten injecte databasetilkoblingen i constructoren til hver klasse som trenger den, eller sette opp en egen method for å 'injecte' den.

 

En child klasse skal kun være en videre spesifisering av parent, du du gjør her er å bruke en parent for å legge til en spesifikk funksjonalitet.

Lenke til kommentar

akkurat... da må jeg spørre videre hvordan jeg går frem for å oppnå minst mulig koding for hver klasse?

Jeg lagrer allerede hver tilkobling til $con i DB klassen. Så det er denne jeg ønsker å gjøre tilgjenglig for andre klasser på ett eller annet sett og vis - slik at jeg kan benytte den ved å skrive con('alias') for å benytte ønsket tilkobling..

 

OPPDATERING:

Så jeg forska litt på det å sette tilkoblingene i hver klasse som skulle benytte seg av database via __construct().

Og dette er hva jeg kom frem til:

class SQL
{
    private $dbh;

    public function __construct($connections){
    /**
     *  siden jeg har flere tilkoblinger må jeg kjøre en forach-loop for å hente ut hvert PDO objekt samt tilknytte den til aliaset
     */
        foreach($connections as $con){
            $this->dbh[$con['alias']] = $con['dbh'];
        }
    }
}

Jeg setter nå tilkoblingene slik (samme som i stad, bare at jeg benytter DB klassen istedenfor):

$_SITE['db']['class'] = new DB();  //  byttet til DB
$_SITE['db']['class'] -> add('alias', 'databasenavn','databasebruker','brukerpassord','valgfri beskrivelse for tilkoblingen');
$_SITE['db']['class'] -> add('alias2', 'databasenavn2','databasebruker2','brukerpassord2','');

igjen så er tilkoblingene tilgjenglig i $con - akkuart som tidligere..

Og for å gjøre tilkoblingen(e) tilgjenglig i SQL klassen må jeg gjøre følgende:

$sql = new SQL($_SITE['db']['class']->con());

I SQL klassen har jeg nå lagt til denne metoden:

public function find($alias,$columns,$table,$where)
{
    $qry = $this->dbh[$alias]->query('SELECT '.$columns.' FROM '.$table.' WHERE '.$where)->fetchColumn();
    return $qry;
}

og kan utføre en spørring slik (bare ett eksempel for å teste):

echo $sql->find('site','felt','tabell','id=8');

Hvis jeg vet at den akutelle klassen kun skal bruke en spesifikk tilkobling kan jeg angi ett alias direkte i con() for å slippe å lage en foreach loop.

 

Dette funker. Men jeg føler det er "mye" unødvendig koding for å oppnå dette resultatet.

Mye mulig jeg tar feil, og at det er bare slik det må være om man ikke skal gå over til globale metoder (static) o.l..

 

Men jeg er fortsatt intressert i å se hva du mente med å sette opp en metode for å 'injecte' tilkoblingene..

Endret av Yawa88
Lenke til kommentar

Det er noen lange og detaljerte poster du skriver her, så jeg gjør kanskje ikke helt rettferdighet ovenfor de med korte svar (er på jobb, så jeg må være litt kortfattet).

 

La meg først si at jeg aldri har hatt behov for å koble til flere ulike databaser, så du må ta rådene mine med en klype salt hva angår akkurat denne problematikken.

 

På et rent logisk nivå ville jeg derimot løst det på følgende måte:

 

1) I en eller annen bootstrap fil setter du opp hver databasetilkobling, dette vil du typisk gjøre som et proxy pattern / lazy loading, slik at kun de aktuelle tilkoblingene vil være i bruk

2) Hver tilkobling lager du som en separat klasse som extender en parent "Connection" klasse med generell funksjonalitet. Dersom alle tilkoblingene er like, kun med ulikt passord/brukernavn, er det nok like greit å lage flere versjoner av en 'Connection klasse', men hver tilkobling skal være en egen instans av connections.

3) Deretter lager du en klasse som fungerer noe ala et proxy pattern eller decorator pattern og gir denne alle instansene av tilkoblingsklassene. La oss kalle denne for 'Decorator' for øyeblikket.

4) Du bruker deretter 'Decorator' videre i hele applikasjonen din. Hovedpoenget er at du alltid vil skrive samme linje med kode for å gjøre en spørring til en gitt database, og du bruker alltid $decorator.

$decorator
->useConnection('connection')
->select('id')
->from('table')
->where('id',1);

Mens jeg skriver dette titter jeg opp på det siste innlegget ditt, og du benytter i prinsippet allerede samme teknikk, om litt uryddig.

 

Det blir en del klasser og filer, men ikke nødvendigvis så mye kode. PHP er kjent og kritiserers for å ha haugevis med layers, men det er nå slik oop i php ser ut.

Lenke til kommentar

Men jeg er fortsatt intressert i å se hva du mente med å sette opp en metode for å 'injecte' tilkoblingene..

Kanskje noe ala dette:

class DB {
    protected $_connections=array();

    public function connection($db){
        if (isset($this->_connections[$db])) return $this->_connections[$db];
    }

    public function connect(){
        // koble til valgt database og lagre i $this->_connections
    }
}

class test {
    protected $_db;

    function __construct(DB $db){
        $this->_db = $db;
    }

    public function getAll(){
       $data = $this->_db->connection('mysql')->getAll('users');
    }
}

$db = new DB; 
$test = new test($db);
Mulig du bør ta en titt på koden til f.eks. Laravel eller andre rammeverk, de har nok løst dette på en mye bedre måte :)

 

Edit: endret eksempel

Endret av Crowly
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...