Los feeds de actividades son una característica realmente interesante que se ve en muchos sitios web diferentes. La idea es que hay una línea de tiempo, por así decirlo, de las cosas que un usuario ha hecho en la aplicación, y luego ese usuario u otros usuarios pueden ver el registro histórico de varias acciones tomadas. En esta pequeña aplicación de foro, podemos configurar una nueva actividad cuando un usuario crea un nuevo hilo. También podríamos configurar una nueva actividad para cuando un usuario crea una nueva respuesta a un hilo. El primer paso para que funcione una fuente de actividad es almacenar correctamente estas acciones en la base de datos a medida que ocurren. Usaremos eventos de modelo y una nueva clase de actividad para ayudarnos a crear esta característica.


Agregar una nueva clase ActivityTest

Continúe y cree una nueva clase de prueba llamada ActivityTest y colóquela en la carpeta Prueba unitaria. Aquí creamos el nuevo archivo y eliminamos el texto estándar de prueba.
código repetitivo de la clase de prueba

Ahora vamos a modificar el método de prueba para que sea test_it_records_activity_when_a_thread_is_created (). Lo que esta prueba va a confirmar es que cuando un usuario crea un hilo, al mismo tiempo también se generará una actividad y se insertará en la base de datos. Aquí está la primera puñalada a ese objetivo.

Ahora, para comenzar, ejecutemos la prueba. Sabemos que fallará, pero los errores nos impulsarán a dar el siguiente paso.

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_it_records_activity_when_a_thread_is_createdPHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

E 1/1 (100%)

Tiempo: 796 ms, memoria: 8,00 MB

Hubo 1 error:

1) TestsFeatureActivityTest :: test_it_records_activity_when_a_thread_is_created
IlluminateDatabaseQueryException: SQLSTATE [HY000]: Error general: 1 no existe tal tabla: actividades (SQL: seleccione el recuento (*) como agregado de "actividades" donde ("tipo" = created_thread y "user_id" = 1 y "subject_id" = 1 y "subject_type" = AppThread))

De buenas a primeras recibimos el error de que la tabla con la que estamos tratando de trabajar en la base de datos no existe. Arreglemos eso.


Crear modelo de actividad y migración

Utilizando artesano podemos hacer un modelo de Actividad y generarnos una migración al mismo tiempo.

vagrant @ homestead: ~ / Code / forumio $ php artisan make: model Activity -m
Modelo creado con éxito.
Migración creada: 2018_01_30_164752_create_activities_table

La tabla ahora está creada, por lo que el primer error debería corregirse. Ejecute la prueba de nuevo y vea qué sigue.

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_it_records_activity_when_a_thread_is_createdPHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

F 1/1 (100%)

Tiempo: 2,44 segundos, memoria: 8,00 MB

Hubo 1 falla:

1) TestsFeatureActivityTest :: test_it_records_activity_when_a_thread_is_created
No se pudo afirmar que una fila en la tabla [actividades] coincide con los atributos {
    "type": "created_thread",
    "user_id": 1,
    "subject_id": 1,
    "subject_type": "Aplicación \ Tema"
}.

La mesa está vacía.

/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:22
/home/vagrant/Code/forumio/tests/Unit/ActivityTest.php:24

¡FALLOS!
Pruebas: 1, afirmaciones: 1, fallos: 1.

Ok, la tabla ahora existe, pero no se insertan datos durante la creación de un hilo. Necesitamos desarrollar esa función ahora.


Usando Eventos de Modelo en Thread.php

Este es un caso de uso perfecto para eventos modelo. En otras palabras, configuramos oyentes para cuando se ejecuta un modelo de Thread y luego tomamos una acción cada vez que ese modelo se activa. Podemos comenzar usando los eventos del modelo dentro del modelo Thread. Agregaremos otro evento de modelo en el método boot () como hemos visto en un tutorial anterior. Este código resaltado a continuación dice que cada vez que se crea un hilo en la base de datos, también se crea inmediatamente una actividad en la base de datos.

¡Excelente! Se ve bien, así que podemos ejecutar la prueba nuevamente y ver cómo lo estamos haciendo.

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_it_records_activity_when_a_thread_is_createdPHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

E 1/1 (100%)

Tiempo: 1,13 segundos, memoria: 8,00 MB

Hubo 1 error:

1) TestsFeatureActivityTest :: test_it_records_activity_when_a_thread_is_created
IlluminateDatabaseEloquentMassAssignmentException: tipo

¡Ups! Parece que estamos recibiendo una excepción de asignación masiva. Arreglo fácil entrante. Deshabilite la asignación masiva en el modelo de actividad así:

Ejecute la prueba nuevamente y observe que todavía recibimos errores.

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_it_records_activity_when_a_thread_is_createdPHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

E 1/1 (100%)

Tiempo: 798 ms, memoria: 8,00 MB

Hubo 1 error:

1) TestsFeatureActivityTest :: test_it_records_activity_when_a_thread_is_created
IlluminateDatabaseQueryException: SQLSTATE [HY000]: Error general: 1 tabla de actividades no tiene ningún tipo de columna con nombre (SQL: insertar en "actividades" ("tipo", "user_id", "subject_id", "subject_type", "updated_at", "created_at" ) valores (created_thread, 1, 1, AppThread, 2018-01-30 17:00:35, 2018-01-30 17:00:35))

Actualizar migración de actividad

¿Qué no hemos hecho todavía? Así es, no hemos definido los campos de la tabla real en el archivo de migración. Entonces, cuando las migraciones se ejecutan durante la ejecución de una prueba, la tabla se crea pero no hay campos de ningún tipo, por lo que las cosas no van a funcionar. No hay problema, creemos ahora el archivo de migración. Necesitamos incluir campos de base de datos para user_id, subject_id, subject_type y type.

Con la migración ahora en su lugar y todo el trabajo realizado hasta ahora, obtenemos nuestra primera prueba de aprobación. ¡Dulce!
prueba de actividad pasando


Hilo de refactorización

En el nivel más básico, tenemos el registro de nueva actividad en la base de datos en funcionamiento. Sería bueno hacer ese código más general, de modo que no solo se puedan registrar los hilos como una actividad, sino también las respuestas como una actividad. Veamos cómo podemos abordar esto. Lo primero que notamos es que ahora mismo subject_type está codificado en 'AppThread'. ¿Qué tal si lo cambiamos por get_class ($ thread)? También podemos intercambiar created_thread por 'created_'. strtolower ((nuevo ReflectionClass ($ hilo)) -> getShortName ()). Esto usará la reflexión para observar la clase que se usa para insertar la actividad y completar la base de datos en consecuencia.

También parece que la prueba de esta función sigue funcionando, por lo que es bueno.

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_it_records_activity_when_a_thread_is_createdPHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

. 1/1 (100%)

Tiempo: 1,14 segundos, memoria: 8,00 MB

OK (1 prueba, 1 afirmación)

Extraer un método al objeto

Ahora lo que podemos hacer es extraer la lógica del evento modelo a un método protegido para que el código sea un poco más claro. Podemos agregar una función protegida llamada recordActivity () a la clase así:

Con eso en su lugar, el evento modelo se puede reducir a esta simple llamada.

Después de agregar un refactor final, ahora tenemos dos métodos protegidos y un evento modelo relacionado con la actividad de grabación. Estas secciones de código se destacan aquí.


Refactorización a un rasgo

A menudo tiene sentido mover el código reutilizable directamente a un rasgo si lo desea. Luego, puede usar ese código en cualquier clase que desee simplemente haciendo uso de una usedeclaración en la clase en particular. Veamos a qué nos referimos.


Paso 1: crear un nuevo archivo de características

crear una nueva clase de rasgo


Paso 2: agregue una usedeclaración al modelo de hilo


Paso 3: Refactorice esto: levante los miembros

refactorizar los miembros

Una vez que se completen esos tres pasos, ahora tendrá un archivo de características que contiene el código siguiente, y los dos métodos que "sacamos" ya no son parte de la clase Thread Model. Muy genial.

Solo para estar seguros de que toda esta refactorización sigue funcionando bien, podemos ejecutar la prueba nuevamente y se ve bien.

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_it_records_activity_when_a_thread_is_createdPHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

. 1/1 (100%)

Tiempo: 794 ms, memoria: 8,00 MB

OK (1 prueba, 1 afirmación)

Refactorizar un evento modelo

En este momento todavía tenemos ese evento modelo en la clase Thread. PHP Storm no puede refactorizar este siguiente fragmento de código, ya que es una especie de truco específico de Laravel. Vamos a tomar el evento modelo de la clase Thread y moverlo a una función protegida en el Trait llamado boot (). Veamos cómo. En el rasgo, podemos agregar este código aquí:

La convención es nombrar la función comenzando con 'boot' seguido del nombre del Trait. Entonces, boot más RecordsActivity da como resultado bootRecordsActivity () en nuestro caso. Laravel puede detectar esta convención y activar el evento del modelo de la misma forma que si se hubiera creado en el modelo original. Lo que es realmente interesante aquí es que el modelo de subprocesos ahora está de vuelta a donde estaba al principio de este tutorial. La única diferencia es que ahora tiene una línea use RecordsActivity;que básicamente activa la grabación de actividad para el modelo de hilo. ¡Resbaloso!


Prueba de actividad de refactorización

Pensaste que habíamos terminado. ¡No somos! También podemos hacer que el Rasgo sea más limpio. Aquí hay una versión más limpia del método getActivityType ().


Refactor polimórfico

Ahora completaremos una refactorización para hacer uso de una relación Polimórfica Muchos. Usamos el método morphMany () para configurar esto. Al proporcionar también el prefijo de 'sujeto', Laravel puede averiguar dinámicamente el subject_id y subject_type correctos para cada actividad.


Clarificación adicional del caso de prueba

La adición a la prueba a continuación establece que la relación de sujeto debe ser válida. En otras palabras, si existe un Modelo de Actividad y solicita el tema de esta actividad, debe ser el hilo.


Añadiendo el método morphTo ()

La prueba anterior no funcionará todavía, porque estamos tratando de usar un método de relación que aún no existe, es decir, sujeto (). Ese método debe agregarse al modelo de actividad de la siguiente manera:


Prueba para grabar actividades de respuesta

No solo queremos que se registre una actividad si un usuario crea un hilo, sino que pasa cuando crea una respuesta. Seguramente queremos incluir respuestas como parte del feed de actividades, así que configurémoslo también.

Primero tenemos la prueba. Cuando se crea una respuesta con la fábrica de modelos, también se crea un hilo base. ¿Entonces que significa eso? Eso significa que debe haber 2 registros de actividad en la base de datos. Este marcado de prueba refleja eso.

Está bien. Entonces podemos ejecutar la prueba.

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_it_records_activity_when_a_reply_is_created
PHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

F 1/1 (100%)

Tiempo: 801 ms, memoria: 8,00 MB

Hubo 1 falla:

1) TestsFeatureActivityTest :: test_it_records_activity_when_a_reply_is_created
No se pudo afirmar que 1 coincide con lo esperado 2.

/home/vagrant/Code/forumio/tests/Unit/ActivityTest.php:38

¡FALLOS!
Pruebas: 1, afirmaciones: 1, fallos: 1.

Bueno, eso nos está dando un gran fracaso. En realidad, todavía no estamos generando registros de actividad para las respuestas. Oh, hombre, ¿eso significa que ahora tenemos que construir el proceso de grabación de actividades de nuevo? Bien adivina que. Dado que usó un rasgo, y dado que ese rasgo aprovecha las relaciones polimórficas, se sorprenderá de lo fácil que es comenzar a registrar actividades en las respuestas. Agregue una palabra a su modelo de respuesta y listo. Echale un vistazo.

Todo lo que hicimos aquí fue simplemente decirle al modelo que use RecordsActivity. Con ese simple cambio, ¡la prueba pasa! También agregaremos favoritos al feed de actividades con este rasgo pronto.

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_it_records_activity_when_a_reply_is_created
PHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

. 1/1 (100%)

Tiempo: 779 ms, memoria: 8,00 MB

OK (1 prueba, 1 afirmación)

Agregar un método getActivitiesToRecord ()

Para agregar la capacidad de rastrear algunos eventos pero no otros en sus modelos, podemos agregar un nuevo método para manejar esto. Una vez que el nuevo método está en su lugar, el método bootRecordsActivity () también se puede actualizar para reflejar este cambio.


Terminando ejecutando todas las pruebas.

Parece que ahora tenemos un problema al ejecutar todas las pruebas.

vagabundo @ homestead: ~ / Código / forumio $ phpunit
PHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

..... E ........ EEEEEEE..E.EEEEEE 31/31 (100%)

Tiempo: 3,69 segundos, memoria: 14,00 MB

Hubo 15 errores:

Quince errores. Ay. En el rasgo RecordsActivity, solo estamos registrando actividades sin tener en cuenta si un usuario está conectado o no. Bueno, la única vez que se debe registrar una actividad es si un usuario está conectado. Entonces podemos actualizar ese rasgo así:

Ahora todas las pruebas pasan.


Eliminar actividades relacionadas

Lo último que debemos cubrir es si un usuario elimina un hilo, ¿eso también elimina alguna actividad asociada? En este momento eso no sucede, pero agregaremos código para asegurarnos de que esto funcione. Primero vamos a actualizar la prueba test_authorized_users_can_delete_threads () en la clase CreateThreadsTest.

Ejecuta la prueba ahora que la hemos modificado:

vagrant @ homestead: ~ / Code / forumio $ phpunit --filter test_authorized_users_can_delete_threads
PHPUnit 6.5.5 por Sebastian Bergmann y colaboradores.

F 1/1 (100%)

Tiempo: 887 ms, memoria: 10,00 MB

Hubo 1 falla:

1) TestsFeatureCreateThreadsTest :: test_authorized_users_can_delete_threads
No se pudo afirmar que una fila de la tabla [actividades] no coincide con los atributos {
    "subject_id": 1,
    "subject_type": "Aplicación \ Tema"
}.

Encontró: [
    {
        "id": "1",
        "user_id": "1",
        "subject_id": "1",
        "subject_type": "Aplicación \ Tema",
        "type": "created_thread",
        "created_at": "2018-01-30 21:15:04",
        "updated_at": "2018-01-30 21:15:04"
    },
    {
        "id": "2",
        "user_id": "1",
        "subject_id": "1",
        "subject_type": "Aplicación \ Responder",
        "type": "created_reply",
        "created_at": "2018-01-30 21:15:04",
        "updated_at": "2018-01-30 21:15:04"
    }
].

/home/vagrant/Code/forumio/vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithDatabase.php:42
/home/vagrant/Code/forumio/tests/Feature/CreateThreadsTest.php:80

¡FALLOS!
Pruebas: 1, afirmaciones: 4, fallos: 1.

¡Hagámoslo pasar!


Agregar un detector de eventos de eliminación en RecordsActivity

Lo primero que haremos es configurar un nuevo detector de eventos en el rasgo RecordsActivity


Actualizar el detector de eventos de eliminación en el modelo de subproceso

Lo anterior se encarga de asegurarse de que se elimine una actividad para un hilo. Ahora también debemos asegurarnos de que la actividad para una respuesta también se elimine. Podemos usar mensajes de orden superior en Laravel Collections para ayudar.

Y ahora, podemos ejecutar todas las pruebas en la suite y estar orgullosos de que todo funcione.
Función de alimentación de actividad de prueba de phpunit


Registrar la actividad del usuario en el resumen de la base de datos

Gran trabajo si lo hizo hasta el final de este tutorial. Cubrimos un montón de funciones que puede usar al configurar el lado del backend de una función de alimentación de actividades para su aplicación. Pudimos ver cómo se podría usar un rasgo junto con eventos de modelo para rastrear la actividad del usuario en la base de datos para su uso posterior.