select model B with model A column name condition in laravel model - sql

I have two models with different name and columns but the purpose/data of the two models is the same, namely, it contains the employee's name, here the model
Employee
name
code
country
city
John
A1
USA
NYC
Doe
A2
USA
LA
New_Employee
v_fullname
v_code
v_country
v_city
Mark
ZZ1
USA
LS
Zuc
FF2
USA
LS
as you can see the column name is different but the purpose is identical. I want to select data from New_Employee but use column name from Employee, so the query will look like this
SELECT v_fullname as name, v_code as code, v_country as country, v_city as city
FROM New_Employee
WHERE name = 'Mark'
Sorry if my explanation is hard to understand, but here's the code I have tried
SyncEmployee model (this model is like a bridge connecting employee and new_employee model)
<?php
namespace App\Models;
use App\Models\Employee;
use App\Models\NewEmployee;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SyncEmployee extends Model
{
use HasFactory;
protected $connection = 'mysql_2';
public const TABLE_NAME = 'new_employee';
public function index()
{
$data = NewEmployee::select('v_fullname as name, v_code as code, v_country as country, v_city as city')->get();
return view('view_name', compact('data'));
}
}
I thought with that code when I call SyncEmployee::where('code', '=', 'ZZ1') from controller, the result will
name
code
country
city
Mark
ZZ1
USA
LS
*The data is from New_Employee but the column name using Employee

You could attempt to use the ability to hide or append attributes at serialization to do most of the work for you. You would need to define accessors and mutators and define what is 'hidden' and 'appended' for serialization:
use Illuminate\Database\Eloquent\Casts\Attribute;
class NewEmployee extends Model
{
...
protected $table = 'new_employee';
protected $hidden = [
...
'v_fullname',
'v_code',
'v_country',
'v_city',
];
protected $appends = [
...
'name',
'code',
'country',
'city',
];
protected function name(): Attribute
{
return Attribute::make(
get: fn () => $this->attributes['v_fullname'] ?? null,
set: fn ($v) => $this->attributes['v_fullname'] = $v
);
}
...
}
If you are not using the Model's data after serialization you can still access these fields:
// will hit the accessor
$newEmployee->code;
// will hit the mutator
$newEmployee->code = 'blah';
Laravel 9.x Docs - Eloquent: Mutators and Casting - Accessors & Mutators
Laravel 9.x Docs - Eloquent: Serialization - Hiding Attributes From JSON
Laravel 9.x Docs - Eloquent: Serialization - Appending Values to JSON

Related

Laravel ,php -artisan suffixe had to my table when i seed it

I have 2 small issues with the "php artisan db:seed" command.
WHen i run the command, i have this error message :
"SQLSTATE[42S02] Base table or view not found : 1146 La table
"bootstrap_template_commerciauxes" n'existe pas ..."
The problem is : my table name is commerciaux, and not commerciauxes.
I checked all my file, my model is Commerciaux.php, my factory CommerciauxFactory.
So ... what kind of sorcely is it ? I'am missing something ?
Secondly, the SQL request from db:seed add some columns i dont want to :
SQLSTATE[42S02]: Base table or view not found: 1146 La table 'bootstrap_template.commerciauxes' n'existe pas (SQL: insert into commerciauxes (nom, prenom, ville, updated_at, created_at) values (Dr. Luis Champlin PhD, Dr. Luella Leuschke, Leathaberg, 2022-06-03 21:42:44, 2022-06-03 21:42:44))
Here is my Commerciaux model :
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Commerciaux extends Model
{
use HasFactory;
protected $fillable = [
'nom',
'prenom',
'ville',
'nbre_commande',
];
}
My CommerciauxFactory (in case)
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class CommerciauxFactory extends Factory
{
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'nom' => $this->faker->name(),
'prenom' => $this->faker->name(),
'ville' => $this->faker->city(),
];
}
}
Thanks you very much for your time, i wanted to try this nice tool but i get blocked since 2 days on thoses mistakes.
To answer your issues:
Laravel by default treats table names as plural due to default conversion and I would advise keeping it that way. If you want to define your own table name then, In your model Class you can define following for your name of table:
protected $table = 'commerciaux';
Also, in your migration's Up function, set your table name like following:
public function up()
{
Schema::create('commerciaux', function (Blueprint $table) {
//Your table columns and structure
});
}
Regarding the additional columns, those are laravel timestamps that keep a track of the timestamps of the record when it was created (created_at) and updated(updated_at) last time. In this case, I would also suggest keeping these fields as they keep a track of record creation and last modifying timestamps.
If you don't want these fields in your table then in your model you can define the following code to exclude the timestamps:
public $timestamps = false;
Other than that, you can also remove following line from your migration:
table->timestamps();
EDIT: Before running migration again, try the roll back command so the created base table and migration records can get deleted from the migrations table.

Laravel 5.5, Display news data from two tables

I am new to laravel and I experience some trouble. I try to obtain data stored in two different tables and display them:
News.php (model)
public static function Data($category) {
$perPage = config('var.news.perPage');
if ($category) {
$news = News::orderBy('id', 'desc')->where('category', $category)->SimplePaginate($perPage);
} else {
$news = News::orderBy('id', 'desc')->SimplePaginate($perPage);
}
return $news;
}
This is how I grab all data from News table which struct is:
id, title, body, created_at updated_at, created_by, updated_by, category
The category column contains values separated by comma, e.g. 1,2,3,4
Now, I have another table, News_Cat which has id, name columns.
In another method I try to grab the filters names against values stored in category column of News table
public static function getFilterNames($id) {
$filters = DB::table('News_Cat')
->select('News_Cat.name as name')
->leftJoin('News', DB::raw('CAST(News_Cat.id as nvarchar)'), DB::raw('ANY(SELECT(News.category))'))
->where('News.id', $id)
->get();
return $filters;
}
However, it completely does not work. What I try to achieve is to display filter name in view.blade as 'name' value for specified filter from News_Cat
#if($news->count())
#foreach($news as $article)
<a href="{{ route('news.show', $article->id) }}" class="item angled-bg" data-filters="{{ $filters }}">
<div class="row">
So as result I would get e.g. data-filters="news, update, hot, latest"> instead data-filters="1,2,3,4">
Thank you
You should use eloquent!
In your News Model
public function getFiltersAttribute(){
$categories = explode(',', $this->category);
return implode(', ', NewsCat::find($categories)->pluck('name')->toArray());
}
then in your view :
{{ $article->filters }}
will output news, update, hot, latest
BUT
You should use a pivot table between your categories and your news, it would be much easier.
This method can't allow you to eager load the relationship and make a request for each news
If you can't change your database structure, I can propose you this:
In the boot method of your AppServiceProvider:
Config::set('tags', NewsCat::all());
THEN
public function getFiltersAttribute(){
$categories = explode(',' $this->category);
return implode(', ', config('tags')->whereIn('id', $categories)->pluck('name')->toArray());
}
MANY TO MANY METHOD
I am using laravel naming convention for the table :
news, categories_news (the pivot), and categories
You will have 2 models : New and Category
In your New Model
public function categories(){
return $this->belongsToMany(Category::class)
}
in your Category Model :
public function news(){
return $this->belongsToMany(New::class);
}
if you are not using laravel naming conventions, you will have to customize these raltionship like this : https://laravel.com/docs/5.5/eloquent-relationships#many-to-many

grails: show field of object in gsp

In my grails project I create instances of an object called Patient, that has got a field city.
class Patient {
String name;
String surname;
String cf;
String address;
String cap;
String city;
String province;
String country;
String phone_1;
String phone_2;
String email;
String vat;
}
When I create a Patient, I store city by its id, but I want that, in show() and list() methods, I see the name of the related city.
The Cities domain class is linked to a table in my db as follows
class Cities {
String cityName;
String capOfCity;
String provinceOfCity;
static constraints = {
}
static mapping = {
table 's_cap'
id column: 'idcap'
cityName column: 'comune'
capOfCity column: 'cap'
provinceOfCity column: 'prov'
version false
}
}
I suppose that I must perform a query to db to get its name by id, but how can I do it in gsp?
With your current approach you can do
def show(){
// look up your patient object
def patient = Patient.get(123)
def patientCityObject = Cities.findByCityName(patient.city)
[patientCityObject: patientCityObject ]
}
GSP:
<p>${patientCityObject.cityName}</p>
However,
If you define the association between your domains then Grails will load your city when you are accessing it. To access you city object associated with Patient, you can define it as follow:
class Patient {
...
Cities city;
...
}
Then when you have the patient object, you can easily access its property city.
def patient = Patient.get(123)
patient.city.cityName
This will give you the city object associated with your patient. Then you can pass that into your GSP and use it.
To learn more about GORM and object relation you can read Object Relational Mapping

How to avoid ImprovedNamingStrategy in joinTable in Grails

I have a legacy database which I can't change and I have this setup
class Foo {
static hasMany = [bars:Bar]
static mapping = {
version false
columns {
id column: "FooId"
color column: "FooColor"
bars joinTable: [name: "FooBar", key: 'FooId', column: 'BarId']
}
transient
def getBarName(){
((Bar)this.bars.toArray()[0]).name
}
}
class Bar {
static hasMany = [foos:Foo]
static belongsTo = [Foo, Baz]
static mapping = {
version false
columns {
id column: "BarId"
name column: "BarName"
}
}
When i try to access the method getBarName() in a controller Hibernate translates the inverse column name to "bar_id". Is there some way to set up a mapping like the one for the id and property columns?
And on a side note. How do i correctly implement getBarName()? Thacan't possibly be the correct implementation...
*EDIT*
----------------------------------------------------------------------
Apparently I was unclear above. The thing is that i already have a join column which has the form
-------------------
|RowId|FooId|BarId|
-------------------
| 1 | abc | 123 |
-------------------
Benoit's answer isn't really applicable in this situation since I want to avoid having a domain object for the joinTable.
*EDIT 2*
----------------------------------------------------------------------
Solved it. Dont understand it though... But split the join table information between the two domain classes and it works...
class Foo {
static hasMany = [bars:Bar]
static mapping = {
version false
columns {
id column: "FooId"
color column: "FooColor"
bars joinTable: [name: "FooBar", key: 'FooId']
}
transient
def getBarName(){
((Bar)this.bars.toArray()[0]).name
}
}
class Bar {
static hasMany = [foos:Foo]
static belongsTo = [Foo, Baz]
static mapping = {
version false
columns {
id column: "BarId"
name column: "BarName"
bars joinTable: [name: "FooBar", key: 'BarId']
}
}
As stated in the documentation Many-to-One/One-to-One Mappings and One-to-Many Mapping :
With a bidirectional one-to-many you can change the foreign key column
used by changing the column name on the many side of the association
as per the example in the previous section on one-to-one associations.
However, with unidirectional associations the foreign key needs to be
specified on the association itself.
Thye given example is:
class Person {
String firstName
static hasMany = [addresses: Address]
static mapping = {
table 'people'
firstName column: 'First_Name'
addresses column: 'Person_Address_Id'
}
}

NHibernate does not filter data base on COORECT TYPE of meta data

I have an interface (IContactable) which is realizing by 3 classes : Person, Department, RestUnit
public interface IContactable
{
Contact Contact { get; set; }
string Title { get; }
int? Id { get; set; }
}
public class Person:IContactable
public class Department:IContactable
public class RestUnit:IContactable
There is another class, Contact, which should maintain which one of these objects are the owner of the contact entity.
A part of Contact mapping which does the job is:
ReferencesAny(p => p.Contactable)
.EntityTypeColumn("ContactableType")
.EntityIdentifierColumn("ContactableId")
.IdentityType<int>()
.AddMetaValue<Person>("Person")
.AddMetaValue<Department>("Department")
.AddMetaValue<RestUnit>("RestUnit");
So that Contact records in database would be like (The types are being saved as string):
X Y ContactableType ContactableId
... ... Person 123
... ... Person 124
... ... Department 59879
... ... RestUnit 65
... ... Person 3333
... ... Department 35564
Everything works just fine but filtering data. When I want to get some particular Contacts, say with Department type, I would write something like :
var contacts = Repository<Contact>.Find(p=>p is Department);
Nhibernate tries to filter data based on ContactableType field with an integer value but the ContactableType column is nvarchar
Generated query by NHibernate :
select .......... from contact.[Contact] where ContactableType=1
Expected query:
select .......... from contact.[Contact] where ContactableType='Department'
So NHibernate kinda using a wrong type. int instead of string.
I think NH is using the index of the object in list which AddMetaValue("Department") has added department type into...
I hope the explanation would be clear enough
I'm using NH3....
any idea?
Have you tried to add an extra line:
ReferencesAny(p => p.Contactable)
.MetaType<string>()