Le query del database dovrebbero essere estratte dalla pagina stessa?

10

Quando scrivo la generazione di pagine in PHP, mi trovo spesso a scrivere una serie di file disseminati di query di database. Ad esempio, potrei avere una query per recuperare alcuni dati su un post direttamente dal database da visualizzare su una pagina, come questo:

$statement = $db->prepare('SELECT * FROM posts WHERE id=:id');
$statement->bindValue(':id', $id, PDO::PARAM_INT);
$statement->execute();
$post = $statement->fetch(PDO::FETCH_ASSOC);
$content = $post['content']
// do something with the content

Queste query rapide e una tantum sono in genere di piccole dimensioni, ma a volte finisco con grandi parti del codice di interazione del database che comincia a sembrare piuttosto disordinato.

In alcuni casi, ho risolto questo problema creando una semplice libreria di funzioni per gestire le mie query db post-correlate, abbreviando quel blocco di codice a un semplice:

$content = post_get_content($id);

E questo è fantastico. O almeno lo è fino a quando ho bisogno di fare qualcos'altro. Forse ho bisogno di ottenere i cinque post più recenti da visualizzare in un elenco. Bene, potrei sempre aggiungere un'altra funzione:

$recent_posts = post_get_recent(5);
foreach ($recent_posts as $post) { ... }

Ma questo finisce con l'usare una query SELECT * , che di solito non ho assolutamente bisogno, ma è spesso troppo complicata per essere ragionevolmente astratta. Alla fine finisco con una massiccia libreria di funzioni di interazione del database per ogni singolo caso d'uso, o una serie di domande confuse all'interno del codice di ogni pagina. E anche una volta che ho costruito queste librerie, mi ritroverò a dover eseguire un piccolo join che non avevo mai usato prima, e improvvisamente ho bisogno di scrivere un'altra funzione altamente specializzata per fare il lavoro.

Certo, potrei usare le funzioni per casi di uso generale e query per interazioni specifiche, ma non appena inizio a scrivere query non elaborate, comincio a tornare in accesso diretto per tutto. Oppure, o diventerò pigro e comincerò a fare cose nei loop di PHP che dovrebbero comunque essere fatti direttamente nelle query MySQL.

Vorrei chiedere a chi ha più esperienza di scrivere applicazioni su Internet: l'incremento della manutenibilità vale le linee extra di codice e le possibili inefficienze che possono essere introdotte dalle astrazioni? O semplicemente sta usando stringhe di query dirette un metodo accettabile per gestire le interazioni con il database?

    
posta Alexis King 27.12.2012 - 06:55
fonte

4 risposte

7

Quando hai troppe funzioni di query specializzate, puoi provare a suddividerle in bit componibili. Ad esempio

$posts = posts()->joinWithComments()->orderBy("post.post_date")->first(5);

Esiste anche una gerarchia di livelli di astrazione che potresti trovare utile da tenere a mente. Hai

  1. API mysql
  2. le tue funzioni mysql, come select ("select * from posts where foo = bar"); o forse più componibile come select("posts")->where("foo = bar")->first(5)
  3. funzioni specifiche per il dominio dell'applicazione, ad esempio posts()->joinWithComments()
  4. funzioni specifiche per una determinata pagina, come commentsToBeReviewed($currentUser)

Paga molto in termini di facilità di manutenzione per rispettare questo ordine di astrazioni. Gli script delle pagine dovrebbero utilizzare solo le funzioni di livello 4, le funzioni di livello 4 dovrebbero essere scritte in termini di funzioni di livello 3 e così via. È vero che questo richiede un po 'più tempo in anticipo, ma ti aiuterà a mantenere costanti i costi di manutenzione nel tempo (al contrario di "oh mio Dio vogliono un altro cambiamento !!!")

    
risposta data 27.12.2012 - 13:19
fonte
5

Separazione delle preoccupazioni è un principio su cui vale la pena leggere, vedere l'articolo di Wikipedia su di esso.

link

Un altro principio che vale la pena di leggere è Coupling:

link )

Hai due distinti dubbi: uno è il marshalling dei dati dal database e il secondo è il rendering di quei dati. In un'applicazione davvero semplice c'è probabilmente poco da preoccuparsi, hai stretto strettamente l'accesso al database e il livello di gestione con il tuo livello di rendering, ma per le piccole app non è un grosso problema. Il problema è che le applicazioni web tendono ad evolversi e, se mai si desidera scalare un'app Web, in qualsiasi modo, ad esempio prestazioni o funzionalità, si riscontrano alcuni problemi.

Diciamo che stai generando una pagina web di commenti generati dagli utenti. Arriva il capo dai capelli a punta e ti chiede di iniziare a supportare app native come iPhone / Android, ecc. Abbiamo bisogno di un output JSON, ora devi estrarre il codice di rendering che stava generando HTML. Quando hai finito, ora hai una libreria di accesso ai dati con due motori di rendering e tutto è a posto, ridimensionato dal punto di vista funzionale. Potresti persino riuscire a tenere tutto separato, ovvero la logica di business dal rendering.

Arriva il capo e ti dice che ha un cliente che vuole mostrare i post sul loro sito web, hanno bisogno di XML e hanno bisogno di circa 5000 richieste al secondo di picco. Ora devi generare XML / JSON / HTML. È possibile separare nuovamente il rendering, come prima. Tuttavia, ora è necessario aggiungere 100 server per ottenere comodamente le prestazioni necessarie. Ora il tuo database viene colpito da 100 server con possibilmente dozzine di connessioni per server, ognuna delle quali è esposta direttamente a tre diverse app con requisiti diversi e richieste diverse ecc. Avere l'accesso al database su ciascuna macchina frontend è un rischio per la sicurezza e una crescita uno ma non ci andrò. Ora devi ridimensionare le prestazioni, ogni app ha requisiti di caching diversi, ad esempio problemi diversi. Puoi provare a gestirlo in uno strato strettamente accoppiato, ovvero il tuo accesso al database / livello di logica aziendale / rendering. Le preoccupazioni di ogni livello stanno ora iniziando a intralciarsi reciprocamente, ovvero i requisiti di memorizzazione nella cache dei dati del database potrebbero essere molto diversi da quelli del livello di rendering, la logica che si ha nel livello aziendale è probabile che sfocerà nel SQL cioè si sposta all'indietro o potrebbe scorrere in avanti nel livello di rendering, questo è uno dei problemi più grandi che ho visto avere tutto in un livello, è come versare cemento armato nella tua applicazione e non in un buon modo.

Esistono metodi standard per affrontare questi tipi di problemi, ad esempio la memorizzazione nella cache HTTP dei servizi Web (squid / yts, ecc.). Caching a livello di applicazione all'interno dei servizi Web stessi con qualcosa come memcached / redis. Incontrerai anche problemi quando inizi a scalare il tuo database, cioè più host di lettura e un master, o dati condivisi tra host. Non si desidera che 100 host gestiscano varie connessioni al proprio database che differiscono in base alle richieste di scrittura o lettura o in un database semplificato se un utente "usera" esegue l'hash in "[table / database] foo" per tutte le richieste di scrittura.

La separazione delle preoccupazioni è tuo amico, scegliere quando e dove farlo è una decisione architettonica e un po 'di arte. Evitare l'accoppiamento stretto di tutto ciò che si evolverà per avere esigenze molto diverse. Esistono molti altri motivi per tenere separate le cose, ovvero semplificare i test, l'implementazione di modifiche, la sicurezza, il riutilizzo, la flessibilità, ecc.

    
risposta data 28.12.2012 - 04:20
fonte
1

Suppongo che quando dici "la pagina stessa" intendi il file sorgente PHP che genera dinamicamente HTML.

Non eseguire query nel database e generare HTML nello stesso file sorgente.

Il file sorgente in cui si esegue una query nel database non è una "pagina", anche se si tratta di un file sorgente PHP.

Nel file sorgente PHP in cui si crea dinamicamente l'HTML, si effettuano solo chiamate alle funzioni definite nel file sorgente PHP a cui si accede al database.

    
risposta data 28.12.2012 - 00:53
fonte
0

Lo schema che utilizzo per la maggior parte dei progetti di scala media è il seguente:

  • Tutte le query SQL sono separate dal codice lato server, in una posizione separata.

    Lavorare con C #, significa usare le classi parziali, cioè mettere le query in un file separato, dato che quelle query saranno accessibili da una singola classe (vedi il codice sotto).

  • Quelle query SQL sono costanti . Questo è importante, poiché impedisce la tentazione di creare query SQL al volo (aumentando così il rischio di SQL injection e allo stesso tempo rende più difficile rivedere le query in un secondo momento).

Approccio attuale in C #

Esempio:

// Demo.cs
public partial class Demo : DataRepository
{
    public IEnumerable<Stuff> LoadStuff(int categoryId)
    {
        return this
            .Query(Queries.LoadStuff)
            .With(new { CategoryId = categoryId })
            .ReadRows<Stuff>();
    }

    // Other methods go here.
}

public partial class Demo
{
    private static class Queries
    {
        public const string LoadStuff = @"
select top 100 [StuffId], [SomeText]
    from [Schema].[Table]
    where [CategoryId] = @CategoryId
    order by [CreationUtcTime]";

        // Other queries go here.
    }
}

Questo approccio ha il vantaggio di avere le query in un file separato. Ciò consente a un DBA di rivedere e modificare / ottimizzare le query e commettere il file modificato sul controllo del codice sorgente senza entrare in conflitto con i commit effettuati dagli sviluppatori.

Un vantaggio correlato è che il controllo del codice sorgente può essere configurato in modo da limitare l'accesso di DBA solo ai file che contengono le query e negare l'accesso al resto del codice.

È possibile farlo in PHP?

PHP manca sia delle classi parziali che delle classi interne, così come è, non può essere implementato in PHP.

Puoi creare un file separato con una classe statica separata ( DemoQueries ) contenente le costanti, dato che la classe sarà accessibile da qualsiasi luogo. Inoltre, per evitare di inquinare l'ambito globale, è possibile inserire tutte le classi di query in uno spazio dei nomi dedicato. Questo creerà una sintassi piuttosto dettagliata, ma dubito che tu possa evitare la verbosità:

namespace Data {
    public class Demo inherit DataRepository {
        public function LoadStuff($categoryId) {
            $query = \Queries\Demo::$LoadStuff;
            // Do the stuff with the query.
        }

        // Other methods go here.
    }
}

namespace Queries {
    public static class Demo {
        public const $LoadStuff = '...';

        // Other queries go here.
    }
}
    
risposta data 28.12.2012 - 04:03
fonte

Leggi altre domande sui tag