How SHOW TABLE sql with TABLE GATEWAY ZF2 - php

How can I 'SHOW TABLE' with tablegateway in Zend Framework 2 ?
I would to execute "show table" sql with tablegateway, can i do it?
In sql I can query the db with this code
"SHOW CREATE TABLE {table_name}"
to have the creation table code.
E.G.
CREATE TABLE `table_name` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date_insert` datetime NOT NULL,
`date_update` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
I have the table model that is declared in this way
class MyTable
{
protected $tableGateway;
public function __construct(TableGateway $tableGateway)
{
$this->tableGateway = $tableGateway;
}
public function fetchAll($where = false)
{
$select = $this->tableGateway->getSql()->select();
if ($where)
$select->where($where);
$resultSet = $this->tableGateway->selectWith($select);
return $resultSet;
}
public function getShowCreateTable()
{
?????
}
}
How can I get the show create table?

Zend\Db\Metadata is the component you're looking for. It allows you to extract data about the structure of the database your adapter is connected to.
There is also a TableGateway feature you can enable that will populate the TableGateway instance with metadata information. See the docs for it in this section
This will give you the raw structural information of the table, but won't automatically construct a CREATE TABLE statement for you. Since the SHOW CREATE TABLE query you mentioned is not universally supported you have two options:
If you only need to support MySQL/MariaDB then the quickest route to what you're looking for is the query method of the adapter.
If you want it to be portable then you'll need to use one of the methods I described above and combine it with Zend\Db\Sql\Ddl to construct a cross-platform CREATE TABLE statement.

Related

Eloquent ORM Many-to-many relationship

I've just started using Eloquent ORM (Without Laravel) and I am having issues with the many to many relationships.
I have a table where I store Families (Article categories), another one for the Articles, and a third one as a "pivot". I want to be able to get all the articles a Family has, and all the families an article belongs to. So I have coded this models.
Families
class Families extends Model {
public $table = 'Families';
public function Articles() {
return $this->belongsToMany('Articles', 'articles_families', 'families_id', 'articles_id');
}
}
Articles
class Articles extends Model {
public $table = 'Articles';
public function Families() {
return $this->belongsToMany('Families', null, 'articles_id', 'families_id');
}
}
Then I am trying to retrieve the data like this:
$families = Families::all();
echo $families[1]->Articles;
However, it just returns an empty array, when it should return a couple of articles. I have tripled checked that all the values are correct in the three tables. If I echo the Eloquent query debugger I can see that it is looking for a null value and I'm pretty sure that's the problem, but I don't quite know how to fix it. Here:
{"query":"select * from `Families`","bindings":[],"time":49.13},{"query":"select `Articles`.*, `articles_families`.`families_id` as `pivot_families_id`, `articles_families`.`articles_id` as `pivot_articles_id` from `Articles` inner join `articles_families` on `Articles`.`id` = `articles_families`.`articles_id` where `articles_families`.`families_id` is null","bindings":[],"time":38.93}
The null value is at the end of the last query.
I just found the solution myself. As my primary key columns are called Id, and Eloquent by default assumes the primary key is called id, I needed to override that by adding a class property protected $primaryKey = "Id"; and it now retrieves the data properly.

Why the search query always use ID?

I'm trying to use ->find()
I want to do query lie this
Where IdBiodata = '$IdBiodata'
Then I try this way :
Route::get('biodata/{IdBiodata}', function ($IdBiodata) {
$databiodata = DB::table('Biodata')->find($IdBiodata);
if(count($databiodata)) {
return view('biodata/vew',compact('databiodata'));
}else{
echo "Data not Found";
}
});
when I run it . I face this
SQLSTATE[42S22]: [Microsoft][SQL Server Native Client 10.0][SQL
Server]Invalid column name 'id'. (SQL: select top 1 * from [Biodata]
where [id] = 1)
how can i fix it ? thanks in advance
My Table structure
CREATE TABLE [dbo].[Biodata](
[IdBiodata] [int] IDENTITY(1,1) NOT NULL,
[Nama] [varchar](max) NULL,
[Umur] [int] NULL,
CONSTRAINT [PK_Biodata] PRIMARY KEY CLUSTERED
(
[IdBiodata] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
your search query written using query builder not using Eloquent ORM.find() function Retrieving A Record By Primary Key and Primary Key always take column name as id untill and unless you can not defined externally.
So $databiodata = DB::table('Biodata')->find($IdBiodata); when you write this it's assume Primary key as id.
in Eloquent ORM you can change Primary Key
class Biodata extends Eloquent {
protected $primaryKey = 'IdBiodata';
}
Now, you can write
Biodata::find($IdBiodata);
if you don't wan to use Eloquent ORM so simply use other function rather than find() use where()
$databiodata = DB::table('Biodata')->where('IdBiodata',$IdBiodata)->get();
SO now Replace Your Code using below code
From this
$databiodata = DB::table('Biodata')->find($IdBiodata);
To
$databiodata = DB::table('Biodata')->where('IdBiodata',$IdBiodata)->get();
find() function always use the primary key .. you can use where() instead of find() if you're going to filter it by other field .. here's an example using your query
$databiodata = DB::table('Biodata')->where('IdBiodata',$IdBiodata)->get();

Two models into one paginated query with eager loaded relations using Laravel

I have two Models that I would like to merge into one timeline. I have been able to do this by creating a View in mysql that normalizes and unions the tables. I created a Model for this view, NewsFeed. This works well if I do not want related Comment model. I have gotten close to this by overriding the getMorphClass method on the model. This allows me to get the related comments for the pictures, but not the posts, because when getMorphClass is called the model doesn't have any data.
I am open to any approach on how to solve this, not just the way I am proposing, but I don't want to pull more data than I have to from the database.
NewsFeed
<?php
namespace App\Users;
use App\Pictures\Picture;
use App\Social\Comments\CommentableTrait;
use App\Posts\Post;
use App\Users\User;
use Illuminate\Database\Eloquent\Model;
class UserFeed extends Model
{
use CommentableTrait;
public function user()
{
return $this->belongsTo(User::class);
}
public function getMorphClass(){
if ($this->type == 'post'){
return Post::class;
}
return Picture::class;
}
}
MySQL View
CREATE VIEW
`user_feeds`
AS SELECT
`posts`.`id` AS `id`,
`posts`.`user_id` AS `user_id`,
'post' AS `type`,
NULL AS `name`,
NULL AS `thumbnail`,
`posts`.`body` AS `body`,
`posts`.`updated_at` AS `updated_at`,
`posts`.`created_at` AS `created_at`
FROM
`posts`
UNION SELECT
`pictures`.`id` AS `id`,
`pictures`.`user_id` AS `user_id`,
'picture' AS `type`,
`pictures`.`name` AS `name`,
`pictures`.`thumbnail` AS `thumbnail`,
`pictures`.`description` AS `body`,
`pictures`.`updated_at` AS `updated_at`,
`pictures`.`created_at` AS `created_at`
FROM
`pictures`;
pictures table
id
user_id
title
img
img_width
img_height
img_other
description
created_at
updated_at
posts
id
user_id
title
body
created_at
updated_at
You are really close with your idea to build a view. In fact, if you create an actual table instead of a view, the solution becomes quite simple.
With a 'FeedItem' polymorph object that points to your Post class or Picture class, you can attach the comments directly to the FeedItem with a hasMany relationship.
class FeedItem extends Model {
use CommentableTrait;
public function feedable()
{
return $this->morphTo();
}
}
class Post extends Model {
public function feeditem()
{
return $this->morphOne('FeedItem', 'feedable');
}
}
class Picture extends Model {
public function feeditem()
{
return $this->morphOne('FeedItem', 'feedable');
}
}
This solution may require some refactoring on your forms since you will need to create a FeedItem entry for each Post entry and Picture entry. Event listeners for Picture::created and Post::created should do the trick (http://laravel.com/docs/5.1/eloquent#events).
Once it's set up, you can use:
FeedItem::with('comments')->orderBy('created_at','desc')->paginate(15);
Although I am not familiar with Laravel nor Eloquent for that matter, Here's my input on this.
Assuming you're getting the output from that SQL view into $Entries
As I understand Eloquent allows you to set the values for yourself, therefore something like this might work for you (I am not sure about the syntax or usage for that matter).
$Collection = [];
foreach( $Entries as $Entry ) {
if( $Entry->type === 'post' ) {
$Collection[] = new Post($Entry->toArray())->with('comments');
}else{
$Collection[] = new Picture($Entry->toArray())->with('comments');
}
}
return $Collection;

Laravel belongsToMany relationship defining local key on both tables

So the belongsToMany relationship is a many-to-many relationship so a pivot table is required
Example we have a users table and a roles table and a user_roles pivot table.
The pivot table has two columns, user_id, foo_id... foo_id referring to the id in roles table.
So to do this we write the following in the user eloquent model:
return $this->belongsToMany('Role', 'user_roles', 'user_id', 'foo_id');
Now this looks for an id field in users table and joins it with the user_id field in the user_roles table.
Issue is I want to specify a different field, other than id to join on in the users table. For example I have bar_id in the users table that I want to use as the local key to join with user_id
From laravel's documentation, it is not clear on how to do this. In other relationships like hasMany and belongsTo we can specify local key and foriegn key but not in here for some reason.
I want the local key on the users table to be bar_id instead of just id.
How can I do this?
Update:
as of Laravel 5.5 onwards it is possible with generic relation method, as mentioned by #cyberfly below:
public function categories()
{
return $this->belongsToMany(
Category::class,
'service_categories',
'service_id',
'category_id',
'uuid', // new in 5.5
'uuid' // new in 5.5
);
}
for reference, previous method:
I assume id is the primary key on your User model, so there is no way to do this with Eloquent methods, because belongsToMany uses $model->getKey() to get that key.
So you need to create custom relation extending belongsToMany that will do what you need.
A quick guess you could try: (not tested, but won't work with eager loading for sure)
// User model
protected function setPrimaryKey($key)
{
$this->primaryKey = $key;
}
public function roles()
{
$this->setPrimaryKey('desiredUserColumn');
$relation = $this->belongsToMany('Role', 'user_roles', 'user_id', 'foo_id');
$this->setPrimaryKey('id');
return $relation;
}
On Laravel 5.5 and above,
public function categories()
{
return $this->belongsToMany(Category::class,'service_categories','service_id','category_id', 'uuid', 'uuid');
}
From the source code:
public function belongsToMany($related, $table = null, $foreignPivotKey = null, $relatedPivotKey = null,
$parentKey = null, $relatedKey = null, $relation = null)
{}
This is a recently added feature. I had to upgrade to 4.1 because I was also looking for this.
From the API documentation:
public BelongsToMany belongsToMany(string $related, string $table = null, string $foreignKey = null, string $otherKey = null, string $relation = null)
The $otherKey and $relation parameters were added in 4.1. Using the $foreignKey and $otherKey parameters allows you to specify the keys on both sides of the relation.
The best way is set the primary key.
class Table extends Eloquent {
protected $table = 'table_name';
protected $primaryKey = 'local_key';
belongsToMany allows to define the name of the fields that are going to store che keys in the pivot table but the method insert always the primary key values into these fields.
You have to:
define in the method belongsToMany the table and the columns;
then using protected $primaryKey = 'local_key'; you can choose which value store.
I recently went through the same problem where I needed to have an associated table that used ID's to link two tables together that were not Primary Keys. Basically what I did was create a copy of my model that models the pivot table and set the Primary Key to the value that I wanted it to use. I tried creating a model instance, settings the primary key and then passing that to the relation but Laravel was not respecting the primary key I had set ( using the ->setPrimaryKey() method above ).
Making a copy of the model and setting the primary key feels a little bit 'hackish' but in the end it works as it should and since Pivot table models are generally very small I don't see it causing any problems in the future.
Would love to see a third key option available in the next release of Laravel that lets you get more specific with your linking.

Laravel: How should I update this relationship?

Suppose I have the following tables:
User:
-userID
-userName
...
Exercises:
-exerciseID
...
User model:
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
class User extends Eloquent implements UserInterface, RemindableInterface {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
protected $primaryKey = 'userID';
...
public function hasPassedExercises() {
return $this->hasMany('Exercise', 'exerciseID');
}
}
I want to say that a User has many completedExercises, so when the user completes an exercise, I update the model like so:
Route::post('dbm/userPassedExercise', function () {
$user = User::with('hasPassedExercises')->find($_POST['userID']);
$exercise = Exercise::find($_POST['exerciseID']);
$user->hasPassedExercises->save($exercise);
});
However, this has no effect on any underlying table, as far as I have understood. I'm trying to make sense of the documentation and see how it applies to my problem. So my question is what is the right course of action to do here.
Should I create a table users_completed_exercises that has userID and exerciseID as foreign keys, and if so, how do I link them to my user when I do the update? Or is there a more elegant solution?
Indeed, you have to use a relationship table (called pivot table).
In the laravel documentation, you have to name your pivot table with your tables name ordered by their name (you have not to, but it's prefered).
We'll take your naming convention so : users_completed_exercises
So here we shoud have this :
users:
- userId // Unsigned Int PRIMARY KEY AUTO_INCREMENT
Exercises:
- exerciseId // Unsigned Int PRIMARY KEY AUTO_INCREMENT
users_completed_exercises:
- id // Unsigned Int PRIMARY KEY AUTO_INCREMENT
- exerciseId // Unsigned Int FOREIGN KEY REFERECES EXERCICES ON ID
- userId // Unsigned Int FOREIGN KEY REFERECES USERS ON ID
On the user model, you should have :
public function passedExercises()
{
// Alphabetical order of your id's are here, very important because laravel
// retreives the good ID with your table name.
return $this->belongsToMany('Exercise', 'users_completed_exercises', 'exerciseId', 'userId');
}
And the inverse on Excercise Model
public function usersWhoPassed()
{
// Alphabetical order of your id's are here, very important because laravel
// retreives the good ID with your table name.
return $this->belongsToMany('User', 'users_completed_exercises', 'exerciseId', 'userId');
}
Retreiving infos are now, so easy.
Route::post('dbm/userPassedExercise', function () {
// Don't use $_POST with laravel, they are exceptions indeed, but avoid as much as
// possible.
$user = User::find(Input::get('userId'));
$exercise = Exercise::find(Input::get('exerciseId'));
// Very important, use () on relationships only if you want to continue the query
// Without () you will get an Exercises Collection. Use ->get() or ->first() to end
// the query and get the result(s)
$exercise->usersWhoPassed()->save($user);
});
You can easly check if user has passed an exercise too
Route::get('/exercises/{id}/passed_users', function($id)
{
$exercise = Exercise::find($id);
if ($exercise->usersWhoPassed()
->where('userId', '=', Input::get('userId'))->count()) {
return 'User has passed';
}
return 'User has failed';
});

Categories