How do I return a union as an Active Record object? - yii2

I have these union in my controller :
$expression = new Expression('"News"');
$featuredNews2= news::find()
->alias('ne')
->select(['ne.title', 'ne.content','ne.featuredOrder', 'category'=>$expression])
->innerJoinWith('featuredOrder1');
$expression2 = new Expression('"Event"');
$featuredEvents2 = event::find()
->select(['ev.title', 'ev.content','ev.featuredOrder','category'=>$expression2])
->from('event ev')
->innerJoinWith('featuredOrder2');
$union = $featuredNews2->union($featuredEvents2);
The relation in model :
news model
public function getFeaturedOrder1()
{
return $this->hasOne(Featured::className(), ['featuredOrder' => 'featuredOrder']);
}
event model
public function getFeaturedOrder2()
{
return $this->hasOne(Featured::className(), ['featuredOrder' => 'featuredOrder']);
}
I need to return the query as an Active Query because I need to access my model's method e.g : $model->featuredOrder1->preview in my view.
The following works but it returns an array, as the result I can't access my model's method :
$unionQuery = (new \yii\db\Query)->select('*')
->from($union)
->orderBy('featuredOrder')->all(\Yii::$app->db2);
I have two questions :
How to return the equivalent $unionQuery above but as an active query object? I have googled and search on SO but what I found is how to return it as array.
This is out of curiosity, I wonder why I should provide my db connection as argument in my $unionQuery all() method above. If I didn't use an argument that point to db2, it will look for table name inside my db database instead ( db is my parent database, this db2 is my module's database/the correct one). This only happen with a union. My news and event model already have this in getdb() function:
return Yii::$app->get('db2');
update
I've tried this too :
$unionProvider = (new ActiveQuery(Featured::className()))->select('*')
->from(['union' => $featuredEvents2->union($featuredNews2)])
->orderBy('featuredOrder');
With this relation in featured model:
public function getNews()
{
return $this->hasOne(News::className(), ['featuredOrder' => 'featuredOrder']);
}
public function getEvents()
{
return $this->hasOne(Event::className(), ['featuredOrder' => 'featuredOrder']);
}
and in the view, I tried this :
foreach($unionProvider as $key=>$model){
echo $model->news->title;
}
but get this error : Trying to get property of non-object
Update 2
I forgot to add ->all() in my $unionProvider, but after that I got this error instead : PHP Fatal Error – yii\base\ErrorException
Allowed memory size of 134217728 bytes exhausted (tried to allocate 12288 bytes)
Might be something wrong with my query? Can't figure it out

You can try using pure SQL. Plus you can test to see if it is returning the correct results and then add it in the statement below.
$customers = Customer::findBySql('SELECT * FROM customer')->all();
Learn more in yii docs

Related

Accessing to the `latest` record stored in DB table right after recording it

In Laravel After recording last row to a DB table, can I safely access same recorded data right after recording it by calling latest() queries? Because transactions by other users may occur at the same time, and it may not really be the last record anymore?
Edit:
For example:
Public function StoreNotif($data){
auth()->user()->Notif()->create(store $data here..)
}
Public function SendNotif(){
$data="123";
$this->StoreNotif($data)
event(new Notification(stored Notif instance?));
}
No, you cannot rely on the database to return the record from your current script.
The ->latest() method will always sort the records with the most recent created_at date first.
https://laravel.com/docs/6.x/queries#ordering-grouping-limit-and-offset
But you haven't provided any code or explanation as to why this is a concern. If you just created a new record, why do you need to query it again? You should already have access to an instance of the model.
EDIT: I've made a few edits to demonstrate how you would pass the model from a controller to an event as referenced in the comments. Please post your code if you want more specific help.
SomeController.php
function store()
{
$model = Model::create([
'some_data' => 1
]);
// fire an event with the newly created model
event(new SomeEvent($model));
dd($model);
}
------------------------
Model {
// ...
attributes: [
'id' => 101,
'some_data' => 1
'created_at' => '2019-10-06 12:48:01',
'updated_at' => '2019-10-06 12:48:01',
]
// ...
}
SomeEvent.php
<?php
namespace App\Events;
use App\Model;
use Illuminate\Queue\SerializesModels;
class SomeEvent
{
use SerializesModels;
public $model;
public function __construct(Model $model)
{
$this->model = $model;
// ...
}
}
EDIT: Per your newly added code, you just need to pass the new model back to the original method. You could do something like this.
Public function StoreNotif($data)
{
// add a return statement
return auth()->user()->Notif()->create(store $data here..);
}
Public function SendNotif()
{
$data="123";
// store the returned data to a variable
$model = $this->StoreNotif($data);
// call the event with the model instance
event(new Notification(model));
}
I'm not sure what 'latest' is but I do know that MySQL uses SELECT LAST_INSERT_ID as the query to get the 'per-connection' id of the last inserted item. Under the covers it's using mysql_insert_id so if you are in a language that supports it, you could use that too.

Intersect or Except methods in linq not working in MYSQL

I am using entity framework with mysql. I have to use Intersect in one of the query but it throws the error "Specified method is not supported.". Can anyone suggest alternatives other than taking data to memory and apply Intersect on that.
public List<share> GetFeed(List<long> UserRules, List<long> ListedShares, string AuthorID)
{
List<share> listshare = DbContext
.shares
.Where(x => UserRules.Intersect(x.share_rules.Select(y => y.rule_id)).Any())
.ToList();
return listshare;
}
Specified method is not supported. means that EntityFramework is not able to translate it to SQL query. Actually, in most cases, it is possible to convert it to Contains, which is supported. You should change your method to match:
public List<share> GetFeed(List<long> UserRules, List<long> ListedShares, string AuthorID)
{
List<share> listshare = DbContext
.shares
.Where(x => x.share_rules.Any(y => UserRules.Contains(y.rule_id)))
.ToList();
return listshare;
}

CakePhp mysql raw query error

I'm new to cakephp. I'm trying to search through mysql tables. I want to use nested query.
class TableController extends AppController{
.
.
public function show(){
$this->set('discouns', $this->DiscounsController->query("SELECT * FROM discoun as Discoun WHERE gcil_id = 1"));//(SELECT id FROM gcils WHERE genre = 'Shoes' AND company_name = 'Adidas')"));
}
}
Error:
Error: Call to a member function query() on a non-object
I've also tried
public function show(){
$this->DiscounsController->query("SELECT * FROM count as Count WHERE ctr_id = (SELECT id FROM ctrs WHERE genre = 'Shoes' AND company_name = 'Adidas')");
}
Error:
Error: Call to a member function query() on a non-object
File: C:\xampp\htdocs\cakephppro\myapp\Controller\CountsController.php
Please help. I've been trying this for last few hours. :/
As mentioned in the comments there are a few problems with your code.
Firstly, you are trying to call the query() method on a Controller, whereas you should be executing it on a Model, as it is models that handle database queries and the controller should simply be used to call these methods to get the data and pass them to the view.
The second thing is that you are executing a very simple SQL query raw instead of using CakePHPs built in functions <- Be sure to read this page in full.
Now for your problem, as long as you have setup your model relationships correctly and followed the correct naming conventions, this should be your code to run your SQL query from that controller:
public function show(){
$this->set('discouns', $this->Discouns->find('all', array(
'conditions' => array(
'gcil_id' => 1,
'genre' => 'shoes',
'company_name' => 'Adidas'
)
));
}
query() is not a Controller, but a Model method. That's what the error (Call to a member function on a non-object) is trying to tell you.
So the correct call would be:
$this->Discount->query()
But you are calling this in a TableController, so unless Table and Discount have some type of relationship, you won't be able to call query().
If the Table does have a relationship defined you will be able to call:
$this->Table->Discount->query()
Please not that query() is only used when performing complex SQL queries in scenarios where the standard methods (find, save, delete, etc.) are less practical.
$this->Counts->find('all',array(
'conditions' => array(
'ctrs.genre' => 'Shoes',
'ctrs.company_name' => 'Adidas'
), 'recursive' => 1
));
The above is with tables named counts and ctrs.
This is assuming you have the model set up to have some sort of relationship between the counts table and the ctrs table. It's kind of hard to tell in your code exactly what you tables are.
The CakePHP book should have all the answers you need. One of the reasons to run CakePHP over regular PHP is the FIND statement. Once you have your models set up correctly, using the find statement should be really easy.
http://book.cakephp.org/2.0/en/models.html

Codeigniter Noob Question--active record and results?

I'm writing a simple app. I need to block user from a page if their credit is < 0.
I have a table "User_profiles" with a "credit" row.
How can I set up a model in conjunction with the controller to send the user to another page if the value of "credit" is 0?
This should be straightforward, but I'm new at the select->where stuff...
It has to be the row of the current user too--I don't know how to traverse arrays very well yet.
Thanks!
Well, the easiest solution would be to just load a different view...
As for the model, it would look like this:
class UserModel extends Model {
public function getUserCredit($id) {
$this->load->database();
//effectively generates: SELECT `credit` FROM `User_profiles` WHERE `id`=$id
$query = this->db->select('credit')->where('id',$id)->get('User_profiles');
//row() executes the query for a single result, returns the credit property
return $query->row()->credit;
}
}
Then in the controller:
class Users extends Controller {
//....
public function credit() {
$this->load->model('userModel','users');
// assuming the session library has been loaded
$user_id = $this->session->userdata('id');
$credit = $this->users->getUserCredit($user_id);
if ($credit == '0') $this->load->view('users/no_credit');
else $this->load->view('users/credit');
}
}
That's untested, but it should at least help you get the idea.
When you request the page /users/credit/1, CI will call the Users::credit(1) action.
It then loads UserModel as $this->users
You call $this->users->getUserCredit(1), which translates to UserModel::getUserCredit(1), to store as $credit
The model loads the database.
You tell the db to select('credit') (select the credit column), where('id',1) (where the id = 1), then get('User_profiles') (get matching rows from the User_profiles table). That returns a query, which you store as $query for readability.
getUserCredit returns the credit property of the single-row result of the query
If $credit == 0, you load the view views/users/no_credit.php
Otherwise, you load the view views/users/credit.php (it's conventional to name the views after the actions they represent and put them in a folder corresponding to the controller)

LINQ 2 SQL Query ObjectDisposed Exception

This one i had today is a strange one.
I have this query in an assembly method.
public Order[] SelectAllOrders()
{
Order[] orders;
using (MyDataContext context = new MyDataContext())
{
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Order>(order => order.OrderDetails);
context.LoadOptions = dlo;
orders = context.Orders.Select(p => p).ToArray();
}
return orders;
}
Supposed i already called the ToArray() the SQL Command executed and gave me the objects i need and i give them to a new Order[] array this should not need the DataContext instance.
While im serializing the Order[] i get from the method return, serializer tries to access the DataContext again and i get an exception that cannot access disposed object.
Tried without the using() statement and works like it should. But, why i get this behavior?
Anyone could give an explanation why deferred loading still remains while I'm calling .ToArray() and assigning new variable with the contents?
The Select(p=>p) achieves very little; you might as well just call:
orders = context.Orders.ToArray();
Re the problem - I would guess that either OrderDetails hasn't really loaded, or it is trying to load some other data lazily. I would suggest investigating by (in a dev session):
Order[] orders;
using (MyDataContext context = new MyDataContext())
{
context.Log = Console.Out; // show me
DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Order>(order => order.OrderDetails);
context.LoadOptions = dlo;
Console.WriteLine("> Calling ToArray");
orders = context.Orders.ToArray();
Console.WriteLine("> ToArray complete");
// TODO: your extra code that causes serialziation, probably
// involving `DataContractSerializer`
Console.WriteLine("> Calling Dispose");
}
With this, you should be able to see any extra database trips that are happning after the ToArray but before the Dispose(). The point being: this data is needed for serialization, so either a: ensure it gets loaded, or b: exclude it from serialization.