I have the following migration:
Schema::create('items', function(Blueprint $table) {
$table->uuid('id')->primary();
// more columns ...
});
Now, we want to add an additional auto-increment column:
Schema::table('items', function(Blueprint $table) {
$table->dropPrimary('id');
$table->rename('id', 'SystemId')->change();
$table->id();
});
Problem: SQLite doesn't allow changing the primary key
Solution: It's recommended to delete the table and create it with the changed schema
Of course, that works in theory but it is anything but DRY to copy the code from our first migration to our second. So my question is: Is there another way to achieve this?
So, I finally came up with a solution that is generic enough to be reusable. Would be great to be included into Laravel, but a package is probably more likely.
use Doctrine\DBAL\Schema\Table;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
class ExtendedSQLiteAlterTableMigration extends Migration
{
public function extendedAlterTable(string $tableName, callable $callback)
{
/** #var \Doctrine\DBAL\Schema\AbstractSchemaManager */
$schemaManager = DB::connection()->getDoctrineSchemaManager();
/** #var \Doctrine\DBAL\Schema\Table */
$table = $this->getTempTable($schemaManager, $tableName);
call_user_func($callback, $table);
$tempName = $table->getName();
//$schemaManager->renameTable($tableName, $tempName);
$schemaManager->createTable($table);
$schemaManager->dropTable($tableName);
$schemaManager->renameTable($tempName, $tableName);
}
private function getTempTable($schemaManager, string $name)
{
$columns = $schemaManager->listTableColumns($name);
$foreignKeys = [];
//if ($this->_platform->supportsForeignKeyConstraints()) {
$foreignKeys = $schemaManager->listTableForeignKeys($name);
//}
$indexes = $schemaManager->listTableIndexes($name);
return new Table("temp_$name", $columns, $indexes, [], $foreignKeys);
}
}
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddAutoIncrementPrimaryKeyToTestTable extends ExtendedSQLiteAlterTableMigration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
$this->extendedAlterTable('test', function(Table $table) {$table->dropPrimaryKey();
$table->addColumn('id', 'bigint', [
'autoincrement' => true,
]);
$table->setPrimaryKey([ 'id' ]);
});
}
}
This follows the instructions on the SQLite website
You can't modify SQLite tables in any significant way after they have been created. As you said the accepted suggested solution is to create a new table with the correct requirements and copy your data into it, then drop the old table. That's the only way to do this.
Official documentation about this: http://sqlite.org/faq.html#q11
Related
I have a Laravel 9 project where I am using UUID's. I have just installed the Spatie Permissions package and followed the instructions to use it with UUID's ... But when I try to assignRole I am getting the following error;
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`project`.`model_has_roles`, CONSTRAINT `model_has_roles_role_id_foreign` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE) (SQL: insert into `model_has_roles` (`model_id`, `model_type`, `role_id`) values (1d6535d1-01f0-43b4-8701-4e3c76ad1587, App\Models\User, 0))
I think it might be something to do with the Spatie migration which comes with the package. I have updated it as per https://spatie.be/docs/laravel-permission/v5/advanced-usage/uuid#content-migrations .. as below;
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Spatie\Permission\PermissionRegistrar;
class CreatePermissionTables extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
$tableNames = config('permission.table_names');
$columnNames = config('permission.column_names');
$teams = config('permission.teams');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
}
Schema::create($tableNames['permissions'], function (Blueprint $table) {
$table->uuid('id'); // permission id
$table->string('name'); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
$table->primary('id');
$table->unique(['name', 'guard_name']);
});
Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
$table->uuid('id'); // role id
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
}
$table->string('name'); // For MySQL 8.0 use string('name', 125);
$table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
$table->timestamps();
$table->primary('id');
if ($teams || config('permission.testing')) {
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
} else {
$table->unique(['name', 'guard_name']);
}
});
Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
$table->uuid(PermissionRegistrar::$pivotPermission);
$table->string('model_type');
$table->uuid($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
} else {
$table->primary([PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
'model_has_permissions_permission_model_type_primary');
}
});
Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
$table->uuid(PermissionRegistrar::$pivotRole);
$table->string('model_type');
$table->uuid($columnNames['model_morph_key']);
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
if ($teams) {
$table->unsignedBigInteger($columnNames['team_foreign_key']);
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
$table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
} else {
$table->primary([PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
'model_has_roles_role_model_type_primary');
}
});
Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
$table->uuid(PermissionRegistrar::$pivotPermission);
$table->uuid(PermissionRegistrar::$pivotRole);
$table->foreign(PermissionRegistrar::$pivotPermission)
->references('id') // permission id
->on($tableNames['permissions'])
->onDelete('cascade');
$table->foreign(PermissionRegistrar::$pivotRole)
->references('id') // role id
->on($tableNames['roles'])
->onDelete('cascade');
$table->primary([PermissionRegistrar::$pivotPermission, PermissionRegistrar::$pivotRole], 'role_has_permissions_permission_id_role_id_primary');
});
app('cache')
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
->forget(config('permission.cache.key'));
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
$tableNames = config('permission.table_names');
if (empty($tableNames)) {
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
}
Schema::drop($tableNames['role_has_permissions']);
Schema::drop($tableNames['model_has_roles']);
Schema::drop($tableNames['model_has_permissions']);
Schema::drop($tableNames['roles']);
Schema::drop($tableNames['permissions']);
}
}
I have a trait for UUID which is as follows;
<?php
declare(strict_types=1);
namespace App\Concerns;
use Illuminate\Support\Str;
trait HasUuid
{
protected static function boot(): void
{
parent::boot();
static::creating(function ($model) {
if (empty($model->{$model->getKeyName()})) {
$model->{$model->getKeyName()} = Str::uuid()->toString();
}
});
}
public function getIncrementing(): bool
{
return false;
}
public function getKeyType(): string
{
return 'string';
}
}
I have this on my User model, and also I have extended the Role and Permission model which now looks like this;
<?php
declare(strict_types=1);
namespace App\Models;
use App\Concerns\HasUuid;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Spatie\Permission\Models\Role as SpatieRole;
class Role extends SpatieRole
{
use HasFactory;
use HasUuid;
}
The code that it is failing on is the following which is a standard user create, but the assignRole is where it seems to be failing;
User::create([
'name' => 'Super Admin',
'email' => 'super-admin#example.com',
'password' => Hash::make('password'),
])->assignRole('super-admin');
Any help would be greatly appreciated.
I managed to figure this out if anyone has the same issue.
As I am extending the Role and Permission model, this needs to be reflected in the config/permission.php file
'models' => [
/*
* When using the "HasPermissions" trait from this package, we need to know which
* Eloquent model should be used to retrieve your permissions. Of course, it
* is often just the "Permission" model but you may use whatever you like.
*
* The model you want to use as a Permission model needs to implement the
* `Spatie\Permission\Contracts\Permission` contract.
*/
'permission' => \App\Models\Permission::class,
/*
* When using the "HasRoles" trait from this package, we need to know which
* Eloquent model should be used to retrieve your roles. Of course, it
* is often just the "Role" model but you may use whatever you like.
*
* The model you want to use as a Role model needs to implement the
* `Spatie\Permission\Contracts\Role` contract.
*/
'role' => \App\Models\Role::class,
],
I know the question has been responded many times, but for some reason I couldn't make it work for my tables, no matter what, and I don't understand why.
I've been trying this for like 4 hours and I couldn't get it done right.
So here are my functions from two models:
ConducatorDoctorat
public function materii()
{
return $this->belongsToMany('App\DomeniuDoctorat', 'profesor_domeniu', 'domeniu_id', 'profesor_id');
}
DomeniuDoctorat
public function materii()
{
return $this->belongsToMany('App\ConducatorDoctorat', 'profesor_domeniu', 'profesor_id', 'domeniu_id');
}
and profesor_domeniu schema:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ProfesorDomeniu extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('profesor_domeniu', function (Blueprint $table) {
$table->unsignedBigInteger('profesor_id');
$table->unsignedBigInteger('domeniu_id');
$table->foreign('profesor_id')
->references('id')->on('conducatori_doctorat')
->onDelete('cascade');
$table->foreign('domeniu_id')
->references('id')->on('domenii_doctorat')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('profesor_domeniu');
}
}
In my controller, I tried so many ways to do it, for example, like this:
public function edit($id)
{
$materii = ConducatorDoctorat::findOrFail($id)->materii()->pluck('domeniu_id');
return view('admin.conducatori_doctorat.edit')->with([
'materii' => $materii
]);
}
but it still doesn't work.
With the given $id, I want to retrieve all the data from profesor_domeniu where profesor_id == $id.
That's all, but I can't get it.
How can this be done and why doesn't my approach work?
//edit for clarity:
conducator_doctorat is where the professors are stored and domenii_doctorat is where their fields are stored.
In profesor_domeniu, I store what each professor teaches, by linking an id from conducatori_doctorat to an id of a field from domenii_doctorat.
//edit2:
materii() means the fields they teach.
//edit3:
My many-to-many relationship with some data added into the pivot table profesor_domeniu.
eager load your relationship on your model :
public function edit($id)
{
$conducatorDoctorat = ConducatorDoctorat::with('materii')->findOrFail($id);
$materii = $conducatorDoctorat->materii;
return view('admin.conducatori_doctorat.edit')->with([
'materii' => $materii
]);
}
you can also request only your materii related to your conducatorDoctorat :
public function edit($id)
{
$materii = DomeniuDoctorat::whereHas('materii', function($query) use ($id){
$query->where('id', $id);
})->get();
return view('admin.conducatori_doctorat.edit')->with([
'materii' => $materii
]);
}
I have two tables that I want to join in the controller - thought_journal_entries and emotions. A thought journal entry can contain many emotions and the foreign key in the thought_journal_entries table is em_id.
This is an example thought journal entry where the user selected emotions with id 1, 3, 5
This is the emotions table
This is the method I'm using to store data within my thought_journal_entries table
public function store(Request $request)
{
$this->validate($request, [
'thought_entry' => 'required'
]);
$entry = new ThoughtJournalEntry;
$entry->user_id = auth()->user()->id;
$entry['entry_date'] = date('Y-m-d H:i');
$entry->thought = $request->input('thought_entry');
$entry->em_id = $request->has('emotions') ? $request->get('emotions') : [];
$entry->tt_id = $request->has('thinking_traps') ? $request->get('thinking_traps') : [];
$entry->balanced_thought = $request->input('balanced_thought');
$entry->save();
return redirect('/dashboard');
}
In your example em_id column it's not a foreign key, it's a string column as I see.
Therefore, you can't execute a JOIN query for these tables. In your case, I can recommend create a third table thought_journal_entry_emotions.
Here example of code for migration file 2020_02_29_143059_create_thought_journal_entry_emotions_table.php:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateThoughtJournalEntryEmotionsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('thought_journal_entry_emotions', function (Blueprint $table) {
$table->integer('thought_journal_entry_id')->unsigned();
$table->integer('emotion_id')->unsigned();
$table->foreign('thought_journal_entry_id')
->references('id')
->on('thought_journal_entries')
->onUpdate('cascade')
->onDelete('cascade');
$table->foreign('emotion_id')
->references('id')
->on('emotions')
->onUpdate('cascade')
->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('thought_journal_entry_emotions');
}
}
Then you have to add relationships to your models Emotion and ThoughtJournalEntry.
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Emotion extends Model
{
public function thoughtJournalEntries() {
return $this->belongsToMany(ThoughtJournalEntry::class, 'thought_journal_entry_emotions',
'emotion_id', 'thought_journal_entry_id');
}
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ThoughtJournalEntry extends Model
{
public function emotions() {
return $this->belongsToMany(Emotion::class, 'thought_journal_entry_emotions',
'thought_journal_entry_id', 'emotion_id');
}
}
After that you can attach Emotions to ThoughtJournalEntry in your controller using this code:
$thoughtJournalEntry = ThoughtJournalEntry::find(1);
$emotion1 = Emotion::find(1);
$emotion2 = Emotion::find(2);
$emotion3 = Emotion::find(3);
$thoughtJournalEntry->emotions()->sync([$emotion1->id, $emotion2->id, $emotion3->id]);
And finally you can load your ThoughtJournalEntry with Emotions in your controller using this code:
$thoughtJournalEntry = ThoughtJournalEntry::with('emotions')->find(1);
dd($thoughtJournalEntry);
If you wanna validate and store Emotions relations you must update your store() method (add new validate rule and sync()).
Here example:
public function store(Request $request)
{
$this->validate($request, [
'thought_entry' => 'required',
'emotions' => 'array|max:3',
'emotions.*' => 'exists:emotions,id'
]);
$entry = new ThoughtJournalEntry;
$entry->user_id = auth()->user()->id;
$entry['entry_date'] = date('Y-m-d H:i');
$entry->thought = $request->input('thought_entry');
$entry->tt_id = $request->has('thinking_traps') ? $request->get('thinking_traps') : [];
$entry->balanced_thought = $request->input('balanced_thought');
$entry->save();
$entry->emotions()->sync($request->get('emotions'));
return redirect('/dashboard');
}
Joining the table will be a little trickier since the reference value isn't present.
But if you trying to get the names of emotions from using the id stored in the array.
You will need to first save the emotions array in a variable.
$em = ["1","3","5"]
$em = ["1","3","5"];
foreach ($em as $e) {
$emotions = Emotions::find($e * 1); //am using * 1 just make sure its int
$emotions->em_name;
}
I hope that helps.
this should done using Many to Many Relationships, you are using string to store array(thats not mysql way). (but looks like you are going to save space in 'thought_journal_entries')
you can use like this:
$journal_entries = thought_journal_entries::find(1);
$icon_ids = json_decode($journal_entries->em_id); // if this column is json
$emocions = emotions::whereIn('id', $icon_ids)->get();
but this executing two quarries, that may affect db and server performance
I am trying to create a many to many relationship between this profile and an availability table but within my test i keep getting call to a undefined method on availability in the test.
This is the controller function
/**
* Creates association between availability and podcast profile
*
* #param array $availabilities
*/
private function associateAvailability(array $availabilities)
{
$this->podcastProfile->availability()->sync(
array_map(function ($availability) {
$availabilityModel = Availability::where('availability', '=', $availability)->first();
return $availabilityModel->id;
}, $availabilities)
);
}
This is the method in the podcast profile model
/**
* Defines many-to-many relationship between podcasts and availabilities
*/
public function availability(): BelongsToMany
{
return $this->belongsToMany(
'App\Models\Availability',
'podcast_availability',
'podcast_profile_id',
'availability_id'
);
}
This is the test for the method
/**
* #test
*/
public function it_should_create_availability_relationship()
{
$this->handlePostRequestToController();
$this->assertTrue($this->user->podcastProfile->availability()->exists());
$this->checkAvailability($this->requestData['availability']);
}
this is the check availability method inserted into the test
/**
* Check database
*
* #param $availabilities
*/
private function checkAvailability($availabilities): void
{
foreach ($availabilities as $availability) {
$availabilityModel = Availability::where('availability', '=', $availability)
->first();
$this->assertDatabaseHas('podcast_availability', [
'podcast_profile_id' => $this->user->podcastProfile->id,
'availability_id' => $availabilityModel->id
]);
}
}
this is the error
1) Tests\Feature\PodcastProfileControllerTest::it_should_create_availability_relationship
BadMethodCallException: Method Illuminate\Database\Eloquent\Collection::availability does not exist.
If your trying to make a Many to Many relationship base on Laravel Many to Many Relationship.
Here's how you do it. You need to have to 2 models and 3 migrations.
FIRST
Your model should look like this:
Profile Model
protected $guarded = [];
public function availabilities() {
return $this->belongsToMany(Availability::class);
}
Note: I use availabilities because it is in a many to many relationship so its a better naming convention.
Availability Model
protected $guarded = [];
public function profiles() {
return $this->belongsToMany(Profile::class);
}
SECOND
Your migration should be like this:
Profile Migration
Schema::create('profiles', function (Blueprint $table) {
$table->bigIncrements('id');
...
$table->timestamps();
});
Availability Migration
Schema::create('availabilities', function (Blueprint $table) {
$table->bigIncrements('id');
...
$table->timestamps();
});
Availability And Profiles Migration
Schema::create('availability_profile', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('availability_id');
$table->unsignedBigInteger('profile_id');
$table->timestamps();
});
Note: I use the availability_profile naming convention in alphabetical order
INFO
You can generate this migration using artisan command like this php artisan make:migration create_availability_profile_table --create=availability_profile
LAST
In you controller you can assign the profile to availability
Controller
Assuming you have record on your database.
public function generateAvailability() {
$profile = Profile::firstOrFail(1);
$role = Role::firstOrFail(1);
$profile->availabilities()->attach($role->id);
dd(profile->availabilities);
}
Note: I use dd(dump and die) to check the record
You can also see this reference and this
I'm trying to make a api that will return the type of a word (noun, pronoun, verb, etc.) after creating that word in the database. But for some reason I am getting a "Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsTo::type()" error when the type method is clearly defined in my vocabulary model. I am not using a many to many relationship but a one to many (that's why I am using hasMany() and belongsTo). Type has many Vocabulary but Vocabulary has only one Type and many VocabularyContents and VocabularyContent has only one vocabulary it is related to. So clearly no many to many relationship. So clearly my question is not a duplicate of Call to undefined method (laravel 5.2)
. Here are parts of the code for the application.
The first model is the type model it allows me to get the "contents" of a type (model not listed here) and the vocabularies that belongs to a specific type.
model-code-listing 1: VocType.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class VocType extends Model
{
public function contents()
{
return $this->hasMany('App\VocTypeContent');
}
public function vocabularies()
{
return $this->hasMany('App\VocVocabulary');
}
}
this second model allows me to create a word in the vocabulary table access its "contents", type and category. This is where the issue lies.
model-code-listing 2: VocVocabulary.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class VocVocabulary extends Model
{
protected $fillable = ['voc_category_id','type_id', 'name', 'context', 'picture'];
public $timestamps = false;
public function contents()
{
return $this->hasMany('App\VocVocabularyContent');
}
public function type()
{
return $this->belongsTo('App\VocType');
}
public function category()
{
return $this->belongsTo('App\VocCategory');
}
}
The third model allows me to create the content of a vocabulary and access it parent vocabulary.
model-code-listing 3: VocVocabularyContent.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class VocVocabularyContent extends Model
{
protected $fillable = ['voc_vocabulary_id','lang_id', 'content', 'context', 'romanization', 'pronunciation', 'audio'];
public $timestamps = false;
public function vocabulary()
{
return $this->belongsTo('App\VocVocabulary');
}
}
below are the three migrations used for the models listed above.
migration-code-listing 1: create_voc_types_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateVocTypesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('voc_types', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('abbreviation');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('voc_types');
}
}
migration-code-listing 2: create_voc_vocabularies_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateVocVocabulariesTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('voc_vocabularies', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('cat_id');
$table->unsignedInteger('type_id');
$table->foreign('cat_id')->references('id')->on('voc_categories')->onDelete('cascade');
$table->foreign('type_id')->references('id')->on('voc_types')->onDelete('cascade');
$table->string('name');
$table->string('context');
$table->string('picture');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('voc_vocabularies');
}
}
migration-code-listing 3: create_voc_vocabulary_contents_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateVocVocabularyContentsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('voc_vocabulary_contents', function (Blueprint $table) {
$table->primary(['voc_id', 'lang_id']);
$table->unsignedInteger('voc_id');
$table->unsignedInteger('lang_id');
$table->foreign('voc_id')->references('id')->on('voc_vocabularies')->onDelete('cascade');
$table->foreign('lang_id')->references('id')->on('languages')->onDelete('cascade');
$table->string('content');
$table->string('context');
$table->string('romanization');
$table->string('pronunciation');
$table->string('audio');
});
}
/**
* Reverse the migrations.
*
* #return void
*/
public function down()
{
Schema::dropIfExists('voc_vocabulary_contents');
}
}
This is the controller where I am calling the type() method of vocabulary. basically I have an html form that sends a post request to this controller's method (postVocabularyAPI) if no id is provided in the request a vocabulary will be created (if the language is english). Then whether or not an id is provided with the request the method will create a vocabulary "content" for the given id (if no id is provided the given id will be the id of the previously created vocabulary). Then the postVocabularyAPI method will return a json response containing the id of the type of the vocabulary.
controller-code-listing 1: Vocabulearn.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Language;
use App\VocTheme;
use App\VocCategory;
use App\VocCategoryContent;
use App\VocVocabulary;
use App\VocVocabularyContent;
use App\VocType;
class Vocabulearn extends Controller
{
//other methods above
public function postVocabularyAPI(Request $request, $language, $theme, $category){
$vocabulary_id = $request->vocabulary_id;
if($vocabulary_id === NULL){
if($language == "english"){
$vocabulary = VocVocabulary::create([
'voc_category_id' => VocCategory::where("slug", $category)->get()->first()->id,
'type_id' => VocType::where("abbreviation", $request->type)->get()->first()->id,
'name' => ucfirst(addslashes($request->translation)),
'context' => $request->context,
'picture' => ''
]);
$vocabulary_id = $vocabulary->id;
} else {
echo '{"success":false, "message":"Create first the English Vocabulary"}';
}
}
$vocabularyContent = VocVocabularyContent::where('lang_id', '=', Language::where("slug", $language)->get()->first()->id)
->where('voc_vocabulary_id', '=', $vocabulary_id)
->first();
if($vocabularyContent !== NULL){
$vocabularies = DB::table('voc_vocabulary_contents')
->where('lang_id', '=', Language::where("slug", $language)->get()->first()->id)
->where('voc_vocabulary_id', '=', $vocabulary_id)
->delete();
}
$vocabularyContent = VocVocabularyContent::create([
'voc_vocabulary_id' => $vocabulary_id,
'lang_id' => Language::where("slug", $language)->get()->first()->id,
'content' => ucfirst(addslashes($translation)),
'context' => addslashes($context),
'romanization' => strtolower(addslashes($romanization)),
'pronunciation' => $pronunciation,
'audio' => $request->audio
]);
echo '{"success":true, "type":"'.stripslashes(html_entity_decode($vocabularyContent->vocabulary()->type()->id)).'"}';
}
}
doing this gives me a
"Call to undefined method Illuminate\Database\Eloquent\Relations\BelongsTo::type()"
even when I change
echo '{"success":true, "type":"'.stripslashes(html_entity_decode($vocabularyContent->vocabulary()->type()->id)).'"}';
by
echo '{"success":true, "type":"'.stripslashes(html_entity_decode($vocabularyContent->vocabulary()->get()->first()->type()->id)).'"}';
I get an error stating
"Call to a member function type() on null"
which isn't right because the database was properly populated so I shouldn't be getting a null vocabulary.
There is a quick solution for that.
First add a foreign key in VocVocabulary model type function
public function type()
{
return $this->belongsTo('App\VocType', 'type_id');
}
And then remove paranthesis
echo $vocabularyContent->type->id;
But it is not the standard way to do that. You need to setup your relations in standard ways to help Laravel to understand your relations.
First you need to change the function name as camelCase of the model name. For example as your type model name is VocType so your type function should be changed as
public function type()
To
public function vocType()
{
return $this->belongsTo('App\VocType'); //you don't need a foreign key here
}
In this case you are telling laravel that the function vocType is targeting VocType model. Furthermore you need to change the foreign key in the table of VocVocabulary from type_id to voc_type_id. In this way Laravel clearly understands your relationship otherwise you need to put extra efforts to teach laravel about your relationships.