2016-01-20 14 views
13

È comodo che il modello laravel fornisca un metodo che possa restituire risultati da un'altra tabella associata.Perché il modello laravel duplica un insieme di dati e come (se possibile) avere solo un set di dati?

Ad esempio, ho una tabella chiamata elemento e un'altra tabella chiamata feedback, in cui la tabella di feedback memorizza il feedback di un articolo nella tabella degli articoli. Quindi, per ottenere il tutto valutazioni di elemento con ID 1, farò:

Item::find(1)->feedback; 

E la seguendo questo la stampa dell'oggetto restituito.

Illuminate\Database\Eloquent\Collection Object 
( [items:protected] => Array 
     (
      [0] => Feedback Object 
       (
        [table:protected] => feedback 
        [connection:protected] => 
        [primaryKey:protected] => id 
        [perPage:protected] => 15 
        [incrementing] => 1 
        [timestamps] => 1 
        [attributes:protected] => Array 
         (
          [id] => 1 
          [rma_id] => 3 
          [item_id] => 8 
          [quo_id] => 0 
          [case_id] => i2eM20160120 
          [line_no] => 000001 
          [content] => test 
          [status] => sent 
          [read] => 0 
          [sender] => Tester 
          [created_at] => 2016-01-20 18:03:44 
          [updated_at] => 2016-01-20 18:03:44 
         ) 

        [original:protected] => Array 
         (
          [id] => 1 
          [rma_id] => 3 
          [item_id] => 8 
          [quo_id] => 0 
          [case_id] => i2eM20160120 
          [line_no] => 000001 
          [content] => test 
          [status] => sent 
          [read] => 0 
          [sender] => Tester 
          [created_at] => 2016-01-20 18:03:44 
          [updated_at] => 2016-01-20 18:03:44 
         ) 

        [relations:protected] => Array 
         (
         ) 

        [hidden:protected] => Array 
         (
         ) 

        [visible:protected] => Array 
         (
         ) 

        [appends:protected] => Array 
         (
         ) 

        [fillable:protected] => Array 
         (
         ) 

        [guarded:protected] => Array 
         (
          [0] => * 
         ) 

        [dates:protected] => Array 
         (
         ) 

        [touches:protected] => Array 
         (
         ) 

        [observables:protected] => Array 
         (
         ) 

        [with:protected] => Array 
         (
         ) 

        [morphClass:protected] => 
        [exists] => 1 
       ) 

     ) 

) 

Funziona bene, e si vede che c'è un solo feedback su elemento con id 1.

Ciò che mi preoccupa è che il set di dati è duplicato in [attributes:protected] e [original:protected]. Questo è solo un caso di test e il caso reale consisterà in migliaia di feedback e avere un dataset duplicato è un enorme spreco di memoria. Il set di dati non viene duplicato se sto usando l'approccio DB::table('table_name'), ma è molto meno conveniente.

Perché laravel ha bisogno di duplicare i dati nel modello?

E c'è un modo per restituire solo un set di dati?

Attualmente sto usando ->toArray() per ridurre i dati non necessari subito dopo la query, ma l'utilizzo della memoria è ancora lì perché laravel sta ancora creando quel set di dati.

+0

Tecnicamente non c'è bisogno di utilizzare Eloquente come ORM con laravel si tratta solo di ciò che è costruito in Se non ti piace il modo in cui Eloquente genera oggetti poi. usa un ORM che ti piace o scrivi il tuo codice e poi disabilita Eloquent. – Pitchinnate

+0

Qualche suggerimento per ORM? @ Pitchinnate – cytsunny

+0

Non sicuro, dovresti vedere tutte le diverse strutture che vengono restituite da ORM differenti per trovarne una che ti piace e che non spreca memoria. Il più grande ORM basato su PHP che io conosca è Doctrine. – Pitchinnate

risposta

4

Mentre è difficile ottenere un buon esempio, consente di impostare gli attributi prima di salvarli definitivamente. Probabilmente va bene se si passano attraverso molte funzioni e infine si controlla se tutto è stato impostato correttamente per il salvataggio finale senza la necessità di memorizzare tutto in variabili separate.

Molto piccolo esempio:

$user = User::find(1); 
print_r($user); 
$user->name = 'John Doe'; 
print_r($user); 
$user->save(); 
print_r($user()); 

Restituisce qualcosa di simile:

prima stampa:

[attributes:protected] => Array 
(
    [id] => 1 
    [name] => 'Jimmy Doe' 
    ... 
) 
[original:protected] => Array 
(
    [id] => 1 
    [name] => 'Jimmy Doe' 
    ... 
) 

seconda stampa:

[attributes:protected] => Array 
(
    [id] => 1 
    [name] => 'John Doe' 
    ... 
) 
[original:protected] => Array 
(
    [id] => 1 
    [name] => 'Jimmy Doe' 
    ... 
) 

Thrid stampa:

[attributes:protected] => Array 
(
    [id] => 1 
    [name] => 'John Doe' 
    ... 
) 
[original:protected] => Array 
(
    [id] => 1 
    [name] => 'John Doe' 
    ... 
) 

Solo dopo che il save() i dati sono effettivamente salvato nel DB.

del Eloquente syncOriginal() è sparato quando un modello è save() 'd:

/** 
* Sync the original attributes with the current. 
* 
* @return $this 
*/ 
public function syncOriginal() 
{ 
    $this->original = $this->attributes; 

    return $this; 
} 
+0

Ora capisco perché è impostato in questo modo, ma è possibile disabilitare questa funzione? (si comporta semplicemente come un oggetto normale, non è necessario salvare il valore precedente, salva solo nel database quando si usa la funzione save()) – cytsunny

+0

Secondo la lib, non c'è alcuna funzione per disabilitare questo. Ma per le prestazioni, puoi limitare il numero di chiavi necessarie ottenendo solo la chiave necessaria, come '' '$ user = User :: where ('name', 'John Doe') -> get ('email');' '' Questo ridurrà la dimensione dell'array. Un'altra opzione è non usare Eloquent come @Pitchinnate riferito. – Th3Alchemist

+0

Qualche suggerimento per ORM che fa ciò che ho menzionato? – cytsunny

3

I dati originali vengono memorizzati in modo da consentire il modello per eseguire il controllo sporco. Il controllo sporco viene utilizzato internamente per gestire gli aggiornamenti del database.

Se un modello non è sporco e si tenta di salvarlo, non verrà eseguito alcun aggiornamento.Se un modello è sporco e si tenta di salvarlo, verranno aggiornati solo i campi sporchi.

Se davvero voleva sbarazzarsi di questo, si potrebbe ignorare le syncOriginal() e syncOriginalAttribute() metodi sul modello. Se lo facessi, significherebbe che il modello sarà sempre considerato sporco. getDirty() restituirà sempre tutti gli attributi e isDirty() restituirà sempre true.

Se si utilizza la data e l'ora, sarà inoltre necessario sovrascrivere il metodo updateTimestamps(), altrimenti i campi updated_at e created_at non verranno mai impostati.

class Feedback extends Model 
{ 
    // ... 

    public function syncOriginal() 
    { 
     return $this; 
    } 

    public function syncOriginalAttribute($attribute) 
    { 
     return $this; 
    } 

    protected function updateTimestamps() 
    { 
     $time = $this->freshTimestamp(); 

     $this->setUpdatedAt($time); 

     if (! $this->exists) { 
      $this->setCreatedAt($time); 
     } 
    } 

    // ... 
} 

Ci possono essere altre ripercussioni che non sono immediatamente evidenti durante la revisione del codice.

Detto questo, però, se si ha questa preoccupazione per la memoria, potrebbe essere necessario avere un secondo pensiero sul proprio approccio e su ciò che si sta tentando di fare. Hai davvero bisogno di caricare migliaia di feedback tutti in una volta? È un'operazione che può essere chunk ed? Questo lavoro sarebbe meglio servito da singoli lavori in coda? ecc ...

3

Questo dovrebbe non essere un problema considerando come PHP funziona internamente. A meno che gli "attributi" non vengano modificati, "attributi" è solo un puntatore all '"originale" (o viceversa), quindi avere entrambi gli array occupa quasi la stessa quantità di memoria di uno solo di essi. Questo è il motivo per cui l'utilizzo della memoria non cambia quando si esegue toArray().

Si prega di consultare questo link per ulteriori informazioni: http://blog.ircmaxell.com/2014/12/what-about-garbage.html

Problemi correlati