Δυναμικό Redirection

Μια ελληνική έκδοση αυτού του topic: http://www.yiiframework.com/forum/index.php?/topic/23188-dynamic-redirection/

Μιας και κανείς άλλος δεν είχε καμία ιδέα πάνω στο θέμα…

Λοιπόν το ζητούμενο είναι να μην γνωρίζεις από πριν που θα γίνει redirect ένα action αλλά αυτό να καθορίζεται με μια παράμετρο.

Βέβαια θα πει κανείς, ότι από κάπου θα πρέπει να αρχίσεις. Δεν είναι δυνατόν το σύστημα να είναι τόσο δυναμικό. Σωστό και αυτό.

Πιο συγκεκριμένα έχω έναν controller που έχει το ρόλο του ευρετηρίου.

Το ευρετήριο χρησιμοποιεί διάφορους τρόπους αλλά πάντα καταλήγει στο ίδιο πράγμα: Να μας δίνει το id ενός ActiveRecord

Όπως καταλαβαίνετε υπάρχουν πολλοί άλλοι controllers που αξιοποιούν το ευρετήριο.

Έτσι το ευρετήριο θα πρέπει να έχει όντως ένα δυναμικό redirection. Δηλαδή στο τέλος να επιστρέφει, να κάνει redirect, εκεί όπου του έχει ζητήσει κάποιος άλλος (controller)

Ο τρόπος που το μοντελοποίησα αυτό ήταν να εφαρμόσω καταρχήν μια παράμετρο parameters στα διάφορα Actions.

Έτσι το params είναι ένα array. Άρα σε μια αλληλουχία κάποιων actions. Action0 -> Action1 -> Action2 το κάθε action

μπορεί να κάνει append κάποιες μεταβλητές όσο προχωράει η διαδικασία.

Διότι όπως είπαμε το δυναμικό redirection το θέλουμε κατά βάση στο τέλος. Δηλαδή να έχει δωθεί η παράμετρος στο Action0 και αυτή να διαιωνιστεί ως το Action2 ώστε μετά το Action2 να πάμε (δυναμικά) κάπου αλλού.

Σημειώστε πως όχι δεν χρησιμοποίησα τα filters του Yii. Η υλοποίηση αυτή ίσως να βοηθούμε αλλά το ερώτημα ως μοντελοποίηση παραμένει το ίδιο.

Σημειώστε πως επίσης δεν λειτούργησα με session variables, αν και ίσως να χρειάζεται τελικά.

Διαισθητικά δεν πιστεύω δυστυχώς πως η λύση μου είναι βιώσιμη. Έχετε καμία άλλη ιδέα ή γνωρίζετε άλλους τρόπους αντιμετώπισης του θέματος πιο κομψά?

Αν έγινα κατανοητός με τα παραπάνω να προσθέσω και αυτό: Φανταστείτε πως όταν ένα action ζητάει μια πληροφορία από έναν controller (π.χ. το ευρετήριο) και γίνεται μια ξεχωριστή ακολουθία από actions για να έχουμε αυτή την πληροφορία, μπορεί και το ίδιο το ευρετήριο να ζητάει μια πληροφορία από έναν ΑΛΛΟ controller!!

Αρχίζει δηλαδή να γίνεται αρκετά εμφωλευμένη η κατάσταση.

Οποιαδήποτε ιδέα είναι ευπρόσδεκτη. Τώρα που είναι στο νεαρό της ηλικίας του σχετικά το project και μπορεί να σωθεί κάνοντας refactor τον κώδικα.

Ευχαριστώ πολύ!

Θα μοιραστώ μια λύση που έδωσα στο παραπάνω πρόβλημα με απλή περιγραφή χωρίς κώδικα. Προσωρινή βέβαια μιας και διαισθάνομαι πως σίγουρα υπάρχει μια πιο διαδεδομένη λύση.

Καταρχήν θα λάβουμε ως παραδοχή ότι δουλεύουμε σε ένα browser tab ώστε να μην δημιουργηθούν παράξενα προβλήματα. Δηλαδή το session μας αρκεί.

Βρισκόμαστε λοιπόν σε ένα action αλλά για να το εκτελέσουμε χρειαζόμαστε ορισμένα δεδομένα που εξαρτώνται από τις επιθυμίες του χρήστη. Οπότε αυτό το action είναι ο caller, αυτός που εκτελεί την κλήση.

Προκειμένου να μην εισάγουμε επιπλέον κώδικα μέσα στο action δημιουργούμε ένα νέο filter.

Η κλήση αυτή θα αποθηκευτεί σε ένα persistence μέρος, δηλαδή στη βάση δεδομένων. Έτσι έχουμε ένα action call στο οποίο αποθηκεύουμε ποιος είναι αυτός που κάλεσε καθώς και το sessionID (εδώ βοηθάει το default scope) στο οποίο αυτό αντιστοιχεί. Επιπλέον σώζουμε αν σε τι κατάσταση βρίσκεται o caller, αν έχουν ολοκληρωθεί όλες οι διαδικασίες ή όχι, με ένα boolean.

O caller όμως ποιους καλεί? Διότι ειναι πολύ πιθανό να μην χρειάζεται μόνο ένα δεδομένο αλλά περισσότερα από ένα. Δηλαδή θέλουμε την δυνατότητα ο caller να καλεί ένα ή περισσότερα άλλα actions. Άρα έχουμε ένα δεύτερο table όπου αποθηκεύονται οι κλήσεις (HAS_MANY) για το εκάστοτε action call, στο οποίο αποθηκεύουμε το action που καλούμε. Επίσης όμως χρειαζόμαστε κάπου να αποθηκεύσουμε τα δεδομένα, το όνομα των δεδομένων και την κατάσταση της συγκεκριμένης κλήσης με ένα boolean.

Είμαστε μέσα στο φίλτρο τώρα (συγκεκριμένα στην preFilter μέθοδο) και ορίζουμε ποιους θέλουμε να καλέσουμε. Αμέσως μετά γίνεται η κλήση. Όπως φαντάζεστε επειδή είναι μια νέα κλήση αυτή η εκτέλεση δεν θα επιστρέψει παρά θα καταλήξει σε ένα redirect.

Το ότι έγινε η κλήση προς ένα action δεν σημαίνει ντε και καλά πως θα πρέπει το ίδιο να απαντήσει με τα δεδομένα που μας ενδιαφέρουν. Αυτό το action μπορεί να είναι η αρχή μιας αλυσίδας από actions, με όποια σύνθετη διαδρομή μπορείτε να φανταστείτε που καταλήγουν να απαντήσουν στην κλήση.

Δεν υπάρχει καμιά εξάρτηση, δεν χρειάζεται να περάσουμε κάτι μέσω GET ή POST πχ. Αυτό που υπάρχει είναι μια αποθηκευμένη κλήση στην βάση δεδομένων που δεν έχει απαντηθεί και καραδοκεί.

Σε κάποιο σημείο λοιπόν βρισκόμαστε σε κώδικα ο οποίος θα ψάξει την πρώτη αναπάντητη τέτοια κλήση, θα την απαντήσει με τα δεδομένα που τώρα είναι διαθέσιμα και θα κάνει ξανά redirect πίσω στον caller.

Αν ο caller όπως αναφέραμε έχει περισσότερες από μία κλήσεις τότε θα πάει να κάνει redirect στην επόμενη κλήση και θα παίξει το παραπάνω σενάριο ομοίως πάλι από την αρχή!

Αν όμως όλες οι κλήσεις του caller έχουν απαντηθεί τότε υπάρχουν όλα τα διαθέσιμα δεδομένα στη βάση και τώρα δεν θα γίνει redirect αλλά ο caller θα έχει τα δεδομένα στη διάθεσή του. Άρα τώρα το φίλτρο ολοκληρώνεται κανονικά και το action εκτελείται όπως προορίζονταν εξαρχής.

Παγίδα: Αν κάποιος χρήστης θελήσει να πατήσει το refresh στη σελίδα που τι γίνεται? Απλά ξαναπερνάει όλο το γολγοθά που πέρασε πριν με τις κλήσεις για να δώσει τα δεδομένα στο action αλλά αυτό δεν είναι καθόλου επιθυμητό. Γι’ αυτό επιπλέον πέρα από το να δίνουμε τα δεδομένα στον controller σε μορφή μεταβλητής τα περνάμε και στο url, δηλαδή στο GET.

Και πάλι επιμένω πως θα υπάρχει κάποιο περισσότερο ώριμο και κομψό pattern για αυτό το γενικό πρόβλημα απλά δεν έχει βρεθεί…

Ελπίζω η περιγραφή μου να βοηθήσει τόσο εσάς όσο και εμένα :)

Όποιος θέλει να δοκιμάσει την παραπάνω θεωρία στην πράξη ανέβασα ένα νεο extension εδώ:

dynamic-redirection

Δεκτό το οποιοδήποτε feedback!

Enjoy :)