Strutturare la logica del codice per gli eventi sul controller laravel

Aug 23 2020

Il codice seguente memorizzerà un'immagine base64 su un altro sito Web e creerà un evento per visualizzare l'immagine su una pagina Vue in tempo reale . Funziona bene e tutto.

La mia domanda è: è un buon modo per farlo o c'è un modo migliore per strutturare il codice? Ho pensato di collocare la logica in un ascoltatore ma non mi sembra che debba usarne una. Per le code, le aggiungerò in seguito.

Controller

public function store(Request $request) { $base64 = $request->image; $image = str_replace('data:image/png;base64,', '', $base64); $imageName = 'draw'.time().'.png';
    $response = $this->guzzleClient()->post(config('archiving.api_postBase64_file'),
        [  
            'multipart' => [
                [
                    'name' => 'file',
                    'contents' => $image ], [ 'name' => 'file_name', 'contents' => $imageName
                ],
                [
                    'name' => 'file_folder',
                    'contents' => 'drawings'
                ],
            ],
        ]
    );
    if($response->getStatusCode() >= 500){ return response($response->getBody(), $response->getStatusCode()); }else{ $drawing = Drawing::create([
            'user_id'=>Auth::id(),
            'file_name'=>$imageName, ]); event(new NewDrawing($drawing));
        return response('Drawing created!',200);
    }
}

Evento NewDrawing

class NewDrawing implements ShouldBroadcastNow
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $drawing; public function __construct(Drawing $drawing)
    {
        $this->drawing = $drawing;
    }
    public function broadcastOn()
    {
        return new Channel('channel-drawing');
    }
 }

Frontend (Vue)

<script>
export default {
    data(){
        return{
            images:'',
        }
    },
    methods:{
        listen() {
            Echo.channel('channel-drawing')
            .listen('NewDrawing', res => {
                this.images.push(res.drawing);
            })  
        }
    },
    mounted() {
        this.listen()
    }
}
</script>

Risposte

4 Nick Aug 24 2020 at 14:33

Sono principalmente un ragazzo di back-end che si fa strada nel codice del front-end. Ecco il mio consiglio:

  • Mantenere il codice del controller il più vicino possibile alla gestione della sola risposta. Delegare la creazione di oggetti alle proprie classi. Pensa alla S in SOLID - Responsabilità Unica.

    public function store(Request $request) { $image = new ImageMeta($base64); $archiver = new ImageArchiver($image); $response = $archiver->post(); if($response->getStatusCode() >= 500){
            return response($response->getBody(), $response->getStatusCode());
        }else{
            $drawing = Drawing::create([ 'user_id'=>Auth::id(), 'file_name'=>$imageName,
            ]);
            //There's a few different ways to look at this which might be part of a larger conversation, 
            // so I won't touch this part yet. But is this something specific to drawings? Or is the broadcast
            // of model creation something that will happen for lots of different models? Regardless, I would 
            // change the name from NewDrawing to something like DrawingCreationBroadcast, or something like that
            // NewDrawing gets confusing since its too close to new Drawing();
            event(new NewDrawing($drawing));
            return response('Drawing created!',200);
        }
     }
    

Classe ImageMeta

    /** For all classes assume property declarations, and getters are included. 
    * Lean toward immutable classes where possible, so don't include setters 
    * unless it becomes necessary. Code re-use != object instance re-use.
    **/

    class ImageMeta {
        public function __construct($base64) {
            $this->image = str_replace('data:image/png;base64,', '', $base64);
            $this->imageName = 'draw'.time().'.png';
        }
    }

ImageArchiver classe

    class ImageArchiver {
        private $config;
        
        public function __construct(ImageMeta $image) { $this->image = $image; $this->makeConfig();
        }
        
        private function makeConfig() {
            $this->config = [ 'multipart' => [ [ 'name' => 'file', 'contents' => $image->getImage()
                    ],
                    [
                        'name' => 'file_name',
                        'contents' => $imageName->getName() ], [ 'name' => 'file_folder', 'contents' => 'drawings' ], ], ] } //use type hinting here. Off the top of my head I don't know the parent class of guzzleClient public function archive($guzzleClient) {
            return $guzzleClient->post($this->config);
        }

    }
1 SᴀᴍOnᴇᴌᴀ Aug 24 2020 at 15:41

In questa presentazione sulla pulizia del codice Rafael Dohms parla di molti modi per mantenere il codice snello, come evitare la elseparola chiave. ( vedi le diapositive qui ).

È saggio evitare la elseparola chiave, specialmente quando non è necessaria, ad esempio quando un blocco precedente contiene returnun'istruzione:

   if($response->getStatusCode() >= 500){ return response($response->getBody(), $response->getStatusCode()); }else{ $drawing = Drawing::create([
           'user_id'=>Auth::id(),
           'file_name'=>$imageName, ]); event(new NewDrawing($drawing));
       return response('Drawing created!',200);
   }

Nel codice Frontend, sembra che imagessia impostato su una stringa vuota:

 data(){
   return{
       images:'',
   }
},

Tuttavia, nel callback di ascolto viene utilizzato come un array:

       Echo.channel('channel-drawing')
       .listen('NewDrawing', res => {
           this.images.push(res.drawing);
       })  

Dovrebbe essere inizializzato come un array in data().

Il metodo viene listen()chiamato in un punto diverso dal mounted()callback? In caso contrario, potrebbe essere più semplice spostare il codice da quel metodo nel mounted()callback.