laravel scout: How to update index in controller - php

Here is my question. I want to update the scout index saved in storage in my controller. Any ideas how to do it?
I am using tntsearch package. I know I can do artisan command in command prompt with $ php artisan scout:import App\\Models\\Paper
But I'm working on a website that everyone can submit their journals in it and I need a powerful search engine on my website. So in this situation, I need to update the index every time a journal submitted. So that everyone can be able to search the journals.
I manage to do a part of this task by making a provider TNTSearchScoutServiceProvider.
here is TNTSearchScoutServiceProvider:
class TNTSearchScoutServiceProvider extends \TeamTNT\Scout\TNTSearchScoutServiceProvider
{
public function boot()
{
$this->app[EngineManager::class]->extend('tntsearch', function ($app) {
$tnt = new TNTSearch();
$driver = config('database.default');
$config = config('scout.tntsearch') + config("database.connections.{$driver}");
$tnt->loadConfig($config);
$tnt->setDatabaseHandle(app('db')->connection()->getPdo());
$this->setFuzziness($tnt);
$this->setAsYouType($tnt);
return new TNTSearchEngine($tnt);
});
// To allow us run commands if we're not running in the console
$this->commands([
ImportCommand::class,
]);
}
}
After adding this provider to config/app.php. In the controller I am using the provider like this:
Artisan::call('tntsearch:import', ['model' => 'App\Models\Paper']);
But this throwes this error:
unlink(C:\wamp64\www\mywbsite\storage/papers.index): Resource temporarily unavailable
Here is what I accomplish so far:
although it throws the error,but I can only get the last updated row in search results and the oldest rows doesn't show up in the search results.
So what are your suggestions? Is it a better way to do this? Or I should check out the site every day and run the artisan commands so that the table can be indexed?

I finally managed to solve this problem:
to update the index in storage you just make a new obj from TNTindexer class; First, you create that index and after that, you select the columns you want to update with query() method. then run() the indexer.Before that make sure to load the configuration. here is the method that I write in the controller:
protected function add_to_search(){
$indexer = new TNTIndexer;
$driver = config('database.default');
$config = config('scout.tntsearch') + config("database.connections.{$driver}");
$indexer->loadConfig($config);
$indexer->createIndex('paper.index');
$indexer->query('SELECT id,title,description,abstract,keywords FROM papers;');
$indexer->run();
}
this way the index always updated through a controller.

Related

Laravel - Eager loaded model function

I am trying to build some chat system using Laravel and i have these 2 models: User and Thread
The User model has Messagable Trait where you can get all the threads with
$user->threads();
I am trying to eager load additional data to the threads array using the following:
$threads = Auth::user()->threads()->with(['participants.user'])->get();
What i am struggling is the Threads model has function to get the latest message from it:
$thread->getLatestMessage();
My question is how can i append this latest message to the upper query i am doing. I was trying something like this but its not ok... I guess im doing something stupid here...
$threads = Auth::user()->threads()->with([
'participants.user',
'latestMessage' => function ($query) {
return $query->getLatestMessageAttribute();
}])->get();
or
$threads = Auth::user()->threads()->with(['participants.user','getLatestMessageAttribute'])->get();
I hope i clarified this ok because i am using a 3rd party package for this system which has these Traits and Thread classes i am using.
SOLUTION
Looks like i had to add append('accessor_name') at the end when getting the collection.
$collection = Auth::user()->relationship()->get()->append('accessor_name');
You can override class .Create new model and extend package model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class Thread extends \Lexx\ChatMessenger\Models\Thread
{
use HasFactory;
protected $appends=['latest_message'];
}
Publish config:
php artisan vendor:publish --provider="Lexx\ChatMessenger\ChatMessengerServiceProvider" --tag="config"
in config/chatmessenger.php
'thread_model' => \App\Models\Thread::class,
Updated
If anyone still not getting get attribute data then dont forget to clear cache
php artisan cache:clear
php artisan optimize
php artisan config:clear
php artisan clear

Dynamically add columns in an existing table on the fly in CakePHP 3

I want to add column in my existing table in CakePHP 3.
My ContactsTable.php file code:
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
use Migrations\AbstractMigration;
class ContactsTable extends Table
{
public function initialize(array $config)
{
$this->addBehavior('Timestamp');
$table = $this->table('contacts');
$table->addColumn('price', 'decimal')->update();
}
}
I have tried as described in CakePHP 3 documentation but I got this error:
Call to a member function addColumn() on a non-object
How do I add columns on-the-fly via the controller?
Code:
<?php
namespace App\Controller;
use Cake\Core\Configure;
use Cake\Network\Exception\NotFoundException;
use Cake\View\Exception\MissingTemplateException;
use Cake\ORM\TableRegistry;
use Cake\Database\Schema\Table;
use Cake\Datasource\ConnectionManager;
use \Migrations\AbstractMigration as AbstractMigration;
use \Phinx\Db\Adapter\MysqlAdapter as MysqlAdapter;
class PagesController extends AppController
{
public function display()
{
$connectionArray = ConnectionManager::get('default')->config();
$connectionArray['pass'] = $connectionArray['password'];
$connectionArray['user'] = $connectionArray['username'];
$connectionArray['name'] = $connectionArray['database'];
$migrationObject = new AbstractMigration(mt_rand());
$migrationObject->setAdapter(new MysqlAdapter($connectionArray));
$tree = $migrationObject->table('tests');
$tree->addColumn('something', 'text')
->update();
}
}
After few hours of Hacking, finally found a way to do it on-the-fly.
Tested in default cakephp 3 (latest - as of today - 2nd June '16)
If you are using a different database adapter, change it to that adapater from MysqlAdapter.
Note to the users:
This is an ugly hack and should be used ONLY if you do not work in
an organization where each migration commit requires peer reference.
mt_rand() must NEVER be used as a version number hack.
There is no canonical way of doing it via the controllers. Update in a datasource MUST always be done modified via migrations - using a proper structure.
Refer to Running Migrations in a non-shell environment and try to create a migrations logs under /config/migrations, that would be more rule-specific-on-the-fly and you will also have logs for peers to review.
Migration plugin also support Running Migrations in a non-shell environment.
Since the release of version 1.2 of the migrations plugin, you can run migrations from a non-shell environment, directly from an app, by using the new Migrations class. This can be handy in case you are developing a plugin installer for a CMS for instance. The Migrations class allows you to run the following commands from the migrations shell: migrate, rollback, markMigrated, status and seed.
Each of these commands has a method defined in the Migrations class.
You can prepare some custom handler which will accept column data from user side and run migration. In this case it could be some form with name and type inputs. Migration will be applied to DB after form with data will be submitted.
Here is how to use it:
use Migrations\Migrations;
$migrations = new Migrations();
// Will return an array of all migrations and their status
$status = $migrations->status();
// Will return true if success. If an error occurred, an exception will be thrown
$migrate = $migrations->migrate();
// Will return true if success. If an error occurred, an exception will be thrown
$rollback = $migrations->rollback();
// Will return true if success. If an error occurred, an exception will be thrown
$markMigrated = $migrations->markMigrated(20150804222900);
// Will return true if success. If an error occurred, an exception will be thrown
$seeded = $migrations->seed();
If you want add new column to product table e.g 'price' and price is a 'decimal' you should go to your project and write this in console:
bin/cake bake migration AddPriceToProducts price:decimal
You can see a new file e.g. Config/Migrations/20160501190410_AddPriceToProducts.php
<?php
use Migrations\AbstractMigration;
class AddPriceToProducts extends AbstractMigration
{
/**
* Change Method.
*
* More information on this method is available here:
* http://docs.phinx.org/en/latest/migrations.html#the-change-method
* #return void
*/
public function change()
{
$table = $this->table('products');
$table->addColumn('price', 'decimal', [
'default' => null,
...
'null' => true,
]);
$table->update();
}
}
and later just launch migrations to add this column to data base, write this in console:
bin/cake migrations migrate

Eloquent, Delete not working, Laravel 5

i'm working on a web app using eloquent and laravel 5. The problem is that i'm trying to delete a row of a table called "Ponderacion" but when i send the ajax delete request, the server stops (it stops the execution of the routed function but the server keeps running) at the line where the delete is, without throwing any errors.
Here is the Model of Ponderacion:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Ponderacion extends Model{
protected $table = 'Ponderacion';
protected $fillable = array('ponderacionidearea', 'ConfiguracionDeExamenifconf', 'peso');
public $timestamps = false;
}
Here is the function in the Controller:
public function deleteConfig(Request $request){
error_log('deleting');
$s_area = Area::where('nombre', '=', $request->input('s_area'))->first();
error_log(count($s_area->Configuracion->first()->Ponderacion));
//DB::statement('delete from Ponderacion where idponderacion = 22');
foreach ($s_area->Configuracion->first()->Ponderacion as $ponderacion){
error_log($ponderacion->peso);
try{
$ponderacion->delete();
}catch(Exception $e){
error_log('failure');
}
}
//$s_area->Configuracion->first()->Ponderacion->delete();
error_log('succesfully deleted');
$s_area->Configuracion->first()->delete();
}
I can succesfully print the property "peso" of ponderacion but i'm unable to delete it. Ponderacion has Foreign Keys to other table but no other table has a reference to Ponderacion. I'm able to delete a row of Ponderacion with DB::statement but that is not secure.Succesfully deleted never shows on console.
Thanks in advance.
For AJAX testing, I always prefer to directly test via a GET request first where possible and then layer on the AJAX POST once I know the underlying code works.
In this case, the server is likely throwing a Fatal Error, which will then return a 500 error for an AJAX request and no further information. There is a good SO question that deals with catching Fatal Errors.
I would check your includes for your class. In Laravel 5, the default namespace is not the global namespace for Controllers. You'll need to add a \ before Exception or add use Exception to the top of your class file.
Two tips as well:
Use SoftDeletes on your model. It's better to track and audit your database records if you never really remove a row. DB storage space is really cheap and Laravel will automatically skip the deleted rows when querying.
Use an IDE for development. A lot of these errors can be caught before run-time by a good compiler.

How to perform a delete operation on a Model?

Coming from a Ruby on Rails experience where you load up the rails console to delete a user or all users. I am new to Laravel 5 and I am looking for something similar to delete a user already in the sqlite3 database.
I see where people are talking about User::find(1)->delete(); to delete a user but where to you put that and run in? Is there a console to perform a delete task in? I would like to know how to delete a user without dropping the table. I do not want to soft delete.
You can put this code for example in controller.
You can use
$user = User::find($id);
$user->delete();
if you don't use SoftDeletingTrait trait or
$user = User::find($id);
$user->forceDelete();
if you do, and you want to really remove user from database, not just hide it from results.
More you can read at Laravel page
in Laravel 5 you can use the destroy method.
$user->destroy($id);
and, sure, you have a command line to do so.
$ php artisan tinker
and you can run for example
>> $var = new App\User;
>> $user= $user->find($id);
>> $user->destroy();
Several ways to do this.
If your controller defines the user as an argument:
public function destroy(User $user)
{
return $user->delete();
}
You can also delete any user by $id:
User::destroy ($id);
Assuming you're wrapping these routes with some security.
Edit: Corrected spelling
You can use bellow example to delete data with multiple
parameters......
>
> tableName::where('field_1','=',$para1)
> ->where('field_2,'=',$para2)
> ->delete();
This still works with laravel 7, i use tinker command line and the delete() method:
php artisan tinker
Now i can run commands directly:
> App\User::find($id)->delete();

Laravel migration transaction

When developing i'm having so many issues with migrations in laravel.
I create a migration. When i finish creating it, there's a small error by the middle of the migration (say, a foreign key constraint) that makes "php artisan migrate" fail. He tells me where the error is, indeed, but then migrate gets to an unconsistent state, where all the modifications to the database made before the error are made, and not the next ones.
This makes that when I fix the error and re-run migrate, the first statement fails, as the column/table is already created/modified. Then the only solution I know is to go to my database and "rollback" everything by hand, which is way longer to do.
migrate:rollback tries to rollback the previous migrations, as the current was not applied succesfully.
I also tried to wrap all my code into a DB::transaction(), but it still doesn't work.
Is there any solution for this? Or i just have to keep rolling things back by hand?
edit, adding an example (not writing Schema builder code, just some kind of pseudo-code):
Migration1:
Create Table users (id, name, last_name, email)
Migration1 executed OK. Some days later we make Migration 2:
Create Table items (id, user_id references users.id)
Alter Table users make_some_error_here
Now what will happen is that migrate will call the first statement and will create the table items with his foreign key to users. Then when he tries to apply the next statement it will fail.
If we fix the make_some_error_here, we can't run migrate because the table "items" it's created. We can't rollback (nor refresh, nor reset), because we can't delete the table users since there's a foreign key constraint from the table items.
Then the only way to continue is to go to the database and delete the table items by hand, to get migrate in a consistent state.
It is not a Laravel limitation, I bet you use MYSQL, right?
As MYSQL documentation says here
Some statements cannot be rolled back. In general, these include data
definition language (DDL) statements, such as those that create or
drop databases, those that create, drop, or alter tables or stored
routines.
And we have a recommendation of Taylor Otwell himself here saying:
My best advice is to do a single operation per migration so that your
migrations stay very granular.
-- UPDATE --
Do not worry!
The best practices say:
You should never make a breaking change.
It means, in one deployment you create new tables and fields and deploy a new release that uses them. In a next deployment, you delete unused tables and fields.
Now, even if you'll get a problem in either of these deployments, don't worry if your migration failed, the working release uses the functional data structure anyway. And with the single operation per migration, you'll find a problem in no time.
I'm using MySql and I'm having this problem.
My solution depends that your down() method does exactly what you do in the up() but backwards.
This is what i go:
try{
Schema::create('table1', function (Blueprint $table) {
//...
});
Schema::create('tabla2', function (Blueprint $table) {
//...
});
}catch(PDOException $ex){
$this->down();
throw $ex;
}
So here if something fails automatically calls the down() method and throws again the exception.
Instead of using the migration between transaction() do it between this try
Like Yevgeniy Afanasyev highlighted Taylor Otwell as saying (but an approach I already took myself): have your migrations only work on specific tables or do a specific operation such as adding/removing a column or key. That way, when you get failed migrations that cause inconsistent states like this, you can just drop the table and attempt the migration again.
I’ve experienced exactly the issue you’ve described, but as of yet haven’t found a way around it.
Just remove the failed code from the migration file and generate a new migration for the failed statement. Now when it fails again the creation of the database is still intact because it lives in another migration file.
Another advantage of using this approach is, that you have more control and smaller steps while reverting the DB.
Hope that helps :D
I think the best way to do it is like shown in the documentation:
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
});
See: https://laravel.com/docs/5.8/database#database-transactions
I know it's an old topic, but there was activity a month ago, so here are my 2 cents.
This answer is for MySql 8 and Laravel 5.8
MySql, since MySql 8, introduced atomic DDL: https://dev.mysql.com/doc/refman/8.0/en/atomic-ddl.html
Laravel at the start of migration checks if the schema grammar supports migrations in a transaction and if it does starts it as such.
The problem is that the MySql schema grammar has it set to false. We can extend the Migrator, MySql schema grammar and MigrationServiceProvider, and register the service provider like so:
<?php
namespace App\Console;
use Illuminate\Database\Migrations\Migrator as BaseMigrator;
use App\Database\Schema\Grammars\MySqlGrammar;
class Migrator extends BaseMigrator {
protected function getSchemaGrammar( $connection ) {
if ( get_class( $connection ) === 'Illuminate\Database\MySqlConnection' ) {
$connection->setSchemaGrammar( new MySqlGrammar );
}
if ( is_null( $grammar = $connection->getSchemaGrammar() ) ) {
$connection->useDefaultSchemaGrammar();
$grammar = $connection->getSchemaGrammar();
}
return $grammar;
}
}
<?php
namespace App\Database\Schema\Grammars;
use Illuminate\Database\Schema\Grammars\MySqlGrammar as BaseMySqlGrammar;
class MySqlGrammar extends BaseMySqlGrammar {
public function __construct() {
$this->transactions = config( "database.transactions", false );
}
}
<?php
namespace App\Providers;
use Illuminate\Database\MigrationServiceProvider as BaseMigrationServiceProvider;
use App\Console\Migrator;
class MigrationServiceProvider extends BaseMigrationServiceProvider {
/**
* Register the migrator service.
* #return void
*/
protected function registerMigrator() {
$this->app->singleton( 'migrator', function( $app ) {
return new Migrator( $app[ 'migration.repository' ], $app[ 'db' ], $app[ 'files' ] );
} );
$this->app->singleton(\Illuminate\Database\Migrations\Migrator::class, function ( $app ) {
return $app[ 'migrator' ];
} );
}
<?php
return [
'providers' => [
/*
* Laravel Framework Service Providers...
*/
App\Providers\MigrationServiceProvider::class,
],
];
Of course, we have to add transactions to our database config...
DISCLAIMER - Haven't tested yet, but looking only at the code it should work as advertised :) Update to follow when I test...
Most of the answers overlook a very important fact about a very simple way to structure your development against this. If one were to make all migrations reversible and add as much of the dev testing data as possible through seeders, then when artisan migrate fails on the dev environment one can correct the error and then do
php artisan migrate:fresh --seed
Optionally coupled with a :rollback to test rolling back.
For me personally artisan migrate:fresh --seed is the second most used artisan command after artisan tinker.

Categories