How to change table of model dynamically in laravel 4.2? - php

It is known that after creating a new model, there is a function to change the table used by the model.
For example,
$example_model = new ExampleModel;
$example_model->setTable('myTable_'.$some_other_variable);
However, if I am finding record from table, is there a way to choose the table before querying the database?
i.e. something like this
$example_model = ExampleModel::setTable('myTable_'.$some_other_variable)->where('myColumn', $variable_to_be_compared)->get();
(Noticed that the following line is not correct. I will says setTable is not a static method)
I have some custom function in my model, so I would prefer not to use DB::table('myTable_'.$some_other_variable).

The problem with the upstream setTable() method is that it returns nothing (void) so even you manage to call it you won't be able to chain it with other methods unless you override it.
// File: ExampleModel.php
/**
* Set the table associated with the model.
*
* #param string $table
* #return self
*/
public function setTable($table)
{
$this->table = $table;
return $this;
}
Then you can do something like this
$example_model = with(new ExampleModel)->setTable('myTable_'.$some_other_variable)->where('myColumn', $variable_to_be_compared)->get();
But since that solution involves writing the method, you could write instead a new static method for the task so you don't need to use helpers
/**
* Change the table associated with the model.
*
* #param string $table
* #param array $attributes
* #return self
*/
public static function changeTable($table, $attributes = [])
{
$instance = new static($attributes);
$instance->setTable($table);
return $instance;
}
Which can be use
$example_model = ExampleModel::changeTable('myTable_'.$some_other_variable)->where('myColumn', $variable_to_be_compared)->get();

Related

Laravel Lighthouse - How to update multiple models

I'm trying to update multiple models with a directive, but the current #update directive does not support multiple ids. I would basically want the #delete directive (where you can use a list of ids). To update multiple models. I'm guessing I could create a custom directive, but it's alot of code there that I can't wrap my head around. I've tried to read the docs to understand how to create a custom directive, but I can't get it to work.
So the DeleteDirective.php got this:
/**
* Bring a model in or out of existence.
*
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
protected function modifyExistence(Model $model): void
{
$model->delete();
}
And I would basically want this (for multiple ids):
/**
* Update a model to read true
*
* #param \Illuminate\Database\Eloquent\Model $model
* #return void
*/
protected function updateRead(Model $model): void
{
$model->update(['read' => true]);
}
By defining a mutation query like this:
type Mutation {
updatePostsToRead(id: [ID!]!): [Post!]! #updateRead
}
And doing a query like this:
{
mutation {
updatePostsToRead(id: [6,8]) {
id
amount
}
}
}
Does anyone know how I would go by doing this? Or can point me in the right direction?
Found a way to do it without creating a custom directive. Just made a custom mutation with php artisan lighthouse:mutation updatePostsToRead.
updatePostsToRead.php:
class updatePostsToRead
{
/**
* Return a value for the field.
*
* #param null $rootValue Usually contains the result returned from the parent field. In this case, it is always `null`.
* #param mixed[] $args The arguments that were passed into the field.
* #param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context Arbitrary data that is shared between all fields of a single query.
* #param \GraphQL\Type\Definition\ResolveInfo $resolveInfo Information about the query itself, such as the execution state, the field name, path to the field from the root, and more.
* #return mixed
*/
public function __invoke(
$rootValue,
array $args,
GraphQLContext $context,
ResolveInfo $resolveInfo
) {
// TODO implement the resolver
\DB::table('posts')
->whereIn('id', $args["ids"])
->update(['read' => true]);
$posts = Post::whereIn('id', $args["ids"])->get();
return $posts;
}
}
Schema:
type Mutation {
updatePostsToRead(ids: [ID]): [Post]
}
Query on client:
mutation{
updatePostsToRead(ids: [2,6,8]) {
id
description
read
}
}

Laravel Observers - Any way to pass additional arguments?

So I'm using the Laravel model event observerables to fire custom event logic, but they only accept the model as a single argument. What I'd like to do is call a custom event that I can also pass some extra arguments to that would in turn get passed to the Observer method. Something like this:
$this->fireModelEvent('applied', $user, $type);
And then in the Observer
/**
* Listen to the applied event.
*
* #param Item $item
* #param User $user
* #param string $type
* #return void
*/
public function applied(Item $item, $user, string $type) {
Event::fire(new Applied($video, $user, $type));
}
As you can see i'm interested in passing a user that performed this action, which is not the one that necessarily created the item. I don't think temporary model attributes are the answer because my additional event logic gets queued off as jobs to keep response time as low as possible. Anyone have any ideas on how I could extend Laravel to let me do this?
My theory would be to do a custom trait that overrides one or more functions in the base laravel model class that handles this logic. Thought I'd see if anyone else has needed to do this while I look into it.
Also here's the docs reference
I've accomplished this task by implementing some custom model functionality using a trait.
/**
* Stores event key data
*
* #var array
*/
public $eventData = [];
/**
* Fire the given event for the model.
*
* #param string $event
* #param bool $halt
* #param array $data
* #return mixed
*/
protected function fireModelEvent($event, $halt = true, array $data = []) {
$this->eventData[$event] = $data;
return parent::fireModelEvent($event, $halt);
}
/**
* Get the event data by event
*
* #param string $event
* #return array|NULL
*/
public function getEventData(string $event) {
if (array_key_exists($event, $this->eventData)) {
return $this->eventData[$event];
}
return NULL;
}

Eloquent Relationships

Long story short: I'm building a "privacy" page where uses can chose what shows up and what does not show up on their profiles.
I am considering having a 1:m table user:privacy and just have entries for the keys they want private. If they don't exist they are public. Hope this makes sense.
Table would be user_privacy and will have 3 columns: id, user_id, privacy_key (string, i.e. email/phone/cell/etc)
Is there a way to simple query by the keys i will define that i can run to determine if the user has a key or not or do i have to go extra lengths to add a function to the user model to do this (trying to avoid, love the magic-ness of eloquent)
Basically i want to have a condition that sounds like "if ($user->privacy->email or $user->privacy->phone)"
Thanks and hope i was clear enough, lol
You could add a function to your user model:
public function isPrivate($attribute){
$privacyAttribute = $this->privacy->first(function($model) use ($attribute){
return $model->key == $attribute; // key being the column in the privacy model
});
return !is_null($privacyAttribute);
}
And then do your if statement this way:
if ($user->isPrivate('email') or $user->isPrivate('phone'))
Or a different implementation (usage is the same)
private $privacyAttributes = null;
public function isPrivate($attribute){
if($this->privacyAttributes == null){
$this->privacyAttributes = $this->privacy()->lists('key');
}
return in_array($attribute, $this->privacyAttributes);
}
User Model header:
/**
* Class User
* #package Interallmas
*/
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
/**
* #var null|array
*/
protected $privacy_keys = NULL;
Privacy Relationship:
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function privacy() {
return $this->hasMany('Interallmas\Privacy');
}
Privacy functions:
/**
* #return bool
*/
public function privacy_initialized() {
return ($this->privacy_keys !== NULL);
}
/**
* #return void
*/
public function initialize_privacy() {
if (!$this->privacy_initialized()) {
$this->privacy_keys = [];
foreach ($this->privacy as $privacy) {
$this->privacy_keys[] = $privacy->privacy_key;
}
}
}
/**
* #param $key
* #return bool
*/
public function isPrivate($key) {
$this->initialize_privacy();
return (in_array($key,$this->privacy_keys));
}
So: Whenever i access the isPrivate($key) method, i cache the result for the next use so i don't hit the server too hard - the function may be accessed once or more - i just query once, the first time. I believe for my needs, this is the best way to do it.
I think a simple count > 0 check should suffice. This requires you to have defined the relationship with the hasMany method for the User Model.
if (count($user->privacy) > 0) {
...
}

Identic lines in each action - should I let them this way or not? - Symfony2

I have a controller which has actions for inserting in the database, updating, deleting and some others, but almost all of the actions contain in them this lines:
$em = $this->getDoctrine()->getEntityManager();
$friend = $em->getRepository('EMMyFriendsBundle:Friend')->find($id);
$user = $this->get('security.context')->getToken()->getUser();
Is this OK, or it's code duplication? I tried to make a property called $em and to have a constructor like this:
public function __construct()
{
$this->em = $this->getDoctrine()->getEntityManager();
}
but it didn't work. As for the queries especially the one with the $id parameter, I don't even know how to separate them in one place, so each action to be able to use them. One way is a function, but is there sense in a function like this? And if yes what should it return? An array?
Please advise me for the optimal way!
What I do, for Symfony2, in the controllers to avoid code duplication is creating a class called Controller.php in which I put the function I often use.
For example :
<?php
namespace YourProject\Bundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller as BaseController;
/**
* Base Controller for xxBundle
*/
class Controller extends BaseController
{
/**
* Get repository
*
* #param string $class class
*
* #return Doctrine\ORM\EntityRepository
*/
protected function getRepository($class)
{
return $this->getDoctrine()->getEntityManager()->getRepository($class);
}
/**
* Set flash
*
* #param string $type type
* #param string $text text
*/
protected function setFlash($type, $text)
{
$this->get('session')->getFlashBag()->add($type, $text);
}
/**
* Returns the pager
*
* #param integer $page Page
* #param integer $perPage Max per page
* #param Doctrine_Query $query Query
*
* #return \Pagination
*/
public function getPager($page = 1, $perPage = 10, $query = null)
{
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$query,
$this->get('request')->query->get('page', 1),
$perPage
);
return $pagination;
}
After creating this controller, you need to make your apps controller extends the controller you've created.
That way, you avoid duplicated code and alias for popular method.
You can do:
private $em;
private $friend;
private $user;
private function init($id==null) {
$this->em = $this->getDoctrine()->getEntityManager();
$this->friend = $id?$this->em->getRepository('EMMyFriendsBundle:Friend')->find($id):null;
$this->user = $this->get('security.context')->getToken()->getUser();
}
Then you can call in your actions
$this->init($id);
or
$this->init();
And you will have
$this->em;
$this->friend;
$this->user;
available. Note that I allowed for the $id parameter not to be set, as I guess that in some actions you will not have it.
If you want this init function to be available in different controllers, create a base controller and extend from it, as suggested in another answer.
The thing you are looking for probably is param converter which maps action param to object directly.
Here is description and some examples:
http://symfony.com/doc/2.0/bundles/SensioFrameworkExtraBundle/annotations/converters.html
edit:
Some more info in an interesting article:
http://www.adayinthelifeof.nl/2012/08/04/multiparamconverter-for-symfony2/
If you have that code only in a couple of controllers you can wrap that code into a protected method for both.
If you think that you can reuse that code in more parts of your application then you should start to think if you need write a validator, use a service or another kind of design

How to implement Repository Pattern in Codeigniter?

When I programmed in ASP.NET MVC, there was a neat pattern called Repository. I want to implment it in Codeigniter but I do not know how. Here is what I actually want:
$mock_repository = new MockRepository();
$mock_repository->add(new Item(‘title1′, ‘description1′, 1));
$mock_repository->add(new Item(‘title2′, ‘description2′, 2));
$mock_repository->add(new Item(‘title3′, ‘description3′, 1));
$controller = new Item_controller($mock_repository);
$items = $controller->get_items_by_user_id(1);
$this->_assert_equals(count($items), 2);
I am using TOAST for Unit Testing. So how do I instantiate a controller within a test? The test is of course, another controller itself.
From what I know, to create a Generic Repository Pattern like in C#, you need 2 things PHP 5.6 dosen't have:
Real Method Overloading.
Generic Interface or Generic Abstract Class in PHP.
Click here for more on Generic Repository Pattern in C#.
However you can still create pseudo method overloading in PHP with the help of magic method __call, and we can type little more code for the generic part of the pattern.
Note: Before creating this pattern in Codeigniter 3.0 you will need to create a table in the database, and create auto loader for folder application/libraries.
First we need to create Interface in application/libraries folder:
<?php
interface IRepository
{
public function getById($id);
public function select($columns);
public function delete($id);
}
Seconde we need to create Abstract Class implementing the Interface and extending the CI_Model to be able to use the Database librarie:
<?php
abstract class Base_repository extends CI_Model implements IRepository
{
/**
* This must be valid table name in the Database.
*
* #var string $table Name of the table.
*/
protected $table;
public function __construct()
{
parent::__construct();
}
/**
* Pseudo method overloading.
* It's called when method is not declared in the abstract class.
*
* #param string $name Name of the method
* #param mixed $arguments Arguments of the method
*/
public function __call($name, $arguments)
{
switch ($name)
{
case 'save':
if ($arguments[0]->id > 0)
{
$this->update($arguments[0]);
}
else
{
$this->insert($arguments[0]);
}
break;
}
}
/**
* Get row with id.
*
* #param integer $id
* #return mixed
*/
public function getById($id)
{
return $this->db->get_where($this->table, ['id' => $id])->row_array();
}
/**
* Select columns.
*
* #param array $columns
* #return mixed
*/
public function select($columns = ['*'])
{
$this->db->select($columns);
return $this->db->get($this->table)->result();
}
/**
* Insert data.
*
* #param object $item
* #return void
*/
private function insert($item)
{
unset($item->id);
$this->db->insert($this->table, $item);
}
/**
* Update data.
*
* #param object $item
* #return void
*/
private function update($item)
{
$this->db->where('id =', $item->id);
unset($item->id);
$this->db->update($this->table, $item);
}
/**
* Delete data.
*
* #param integer $id
* #return void
*/
public function delete($id)
{
$this->db->delete($this->table, ['id' => $id]);
}
}
Third test the repository. Make a new model in application/model, and extend Base_repository, set table name and overload save method, create entity for this model:
<?php
/**
* The entity class.
*/
class Test
{
public $id;
public $info;
}
class Test_model extends Base_repository
{
/**
* Tell what table we are using.
*/
public function __construct()
{
parent::__construct();
$this->table = 'test';
}
/**
* "Overload" save method and call it from the parent.
*
* #param test $item Make use of the Dependency Injection.
* #return void
*/
public function save(Test $item)
{
parent::save($item);
}
}
Try it in the controller. Load the model and try to get, insert, ect...
To create real models is the same procedure. If you need to add more methods that will be the same for every model add them in the abstract class if you need to create methods only for specific model add it only in this model.
I don't recommend Codeigniter freamwork. Here are some patterns for PHP CLICK!
You would have to completely hijack the system files to load a controller from another controller. It can't be done, methinks.
It can be done with HMVC.
$result = Modules::run('controller/get_items_by_user_id', $params);
$this->_assert_equals($result, $expected);

Categories