Propel auto generated classes - propel

I have given a project that is written in codeIgnotor and Propel 1.6 as ORM. The only ORM I have used is Eloquent so its really confusing for me to see all these classes in Propel.
When I run propel bat file with defined schema I get three classes e.g
For user I get
1 - User.php extends BaseUser
2 - UserPeer.php extends BaseUserPeer
3 - UserQuery.php extends BaseUserQuery
According to propel documentation Peer Class is for backward compatibility what does that mean.
And also If some one could please explain what type of fuctions one would define inside each class.
Also there is om folder in auto generated build. Is it a good idea to add your method inside these classes.
Any help would be realllly appreciated. If someone could use User example to explain.

Here's a very good Propel article for that: Propel By Example
User.php represents a row in the User table, and your query/update logic in UserQuery.php
The om folder contains class definitions from which your "User" is extended, so don't touch the folder, it gets overwritten everytime you build your model
Examples for #2 above:
Scenario #1: When a User is updated, set TimeUpdated column to the unix current time.
Scenario #2: You want be able to get a Users Full Name
Implementation:
<?php
class User extends BaseUser
{
public function preUpdate(PropelPDO $oConn=null)
{
$this->setTimeUpdated(time());
return true;
}// Will be executed everytime you call '->save()' on an existing row
public function getName()
{
return $this->getFirstName() . ' ' . $this->getLastName();
}
...
Usage for scenario #2:
$User = UserQuery::create()->findPk(1);
echo 'FirstName: ' . $User->getName();
Scenario #3: You want to add a filter for Male teen users
Implementation:
<?php
class UserQuery extends BaseUserQuery
{
public function MaleTeen()
{
return $this
->filterByGender('M')
->filterByAge([
'min' => 13,
'max' => 19
]);
}
...
Usage: $Teens = UserQuery::create()->MaleTeen()->find(); // Did someone say D.R.Y?
I hope it helps.

Related

Zend Framework 2 - Doctrine - Iterate all entity classes [duplicate]

This is probably pretty simple, but I can't find a way to do this.
Is there any way to get a list of class names of the entities that Doctrine manages? Something like:
$entities = $doctrine->em->getEntities();
where $entities is an array with something like array('User', 'Address', 'PhoneNumber') etc...
I know this question is old, but in case someone still needs to do it (tested in Doctrine 2.4.0):
$classes = array();
$metas = $entityManager->getMetadataFactory()->getAllMetadata();
foreach ($metas as $meta) {
$classes[] = $meta->getName();
}
var_dump($classes);
Source
Another way to get the class names of all entities (with namespace) is:
$entitiesClassNames = $entityManager->getConfiguration()->getMetadataDriverImpl()->getAllClassNames();
Unfortunately not, your classes should be organized in the file structure though. Example: a project i'm working on now has all its doctrine classes in an init/classes folder.
There is no built function. But you can use marker/tagger interface to tag entity classes that belong to your application. You can then use the functions "get_declared_classes" and "is_subclass_of" find the list of entity classes.
For ex:
/**
* Provides a marker interface to identify entity classes related to the application
*/
interface MyApplicationEntity {}
/**
* #Entity
*/
class User implements MyApplicationEntity {
// Your entity class definition goes here.
}
/**
* Finds the list of entity classes. Please note that only entity classes
* that are currently loaded will be detected by this method.
* For ex: require_once('User.php'); or use User; must have been called somewhere
* within the current execution.
* #return array of entity classes.
*/
function getApplicationEntities() {
$classes = array();
foreach(get_declared_classes() as $class) {
if (is_subclass_of($class, "MyApplicationEntity")) {
$classes[] = $class;
}
}
return $classes;
}
Please note that my code sample above does not use namespaces for the sake simplicity. You will have to adjust it accordingly in your application.
That said you did't explain why you need to find the list of entity classes. Perhaps, there is a better solution for what your are trying to solve.

Counting page views with Laravel

I want to implement page view counter in my app. What I've done so far is using this method :
public function showpost($titleslug) {
$post = Post::where('titleslug','=',$titleslug)->firstOrFail();
$viewed = Session::get('viewed_post', []);
if (!in_array($post->id, $viewed)) {
$post->increment('views');
Session::push('viewed_post', $post->id);
}
return view('posts/show', compact('post', $post));
}
I retrieve the popular posts list like this :
$popular_posts = Post::orderBy('views', 'desc')->take(10)->get();
However, I'd like to know if there are any better ways to do this ? And with my current method, can I get a list of most viewed posts in the past 24 hours ? That's all and thanks!
As quoted in # milo526's comment, you can record all hits to your pages in a unique way instead of an increment. With this you have many possibilities to search for access information, including the listing of the posts sorted by most viewed.
Create a table to save your view records:
Schema::create("posts_views", function(Blueprint $table)
{
$table->engine = "InnoDB";
$table->increments("id");
$table->increments("id_post");
$table->string("titleslug");
$table->string("url");
$table->string("session_id");
$table->string("user_id");
$table->string("ip");
$table->string("agent");
$table->timestamps();
});
Then, create the corresponding model:
<?php namespace App\Models;
class PostsViews extends \Eloquent {
protected $table = 'posts_views';
public static function createViewLog($post) {
$postsViews= new PostsViews();
$postsViews->id_post = $post->id;
$postsViews->titleslug = $post->titleslug;
$postsViews->url = \Request::url();
$postsViews->session_id = \Request::getSession()->getId();
$postsViews->user_id = \Auth::user()->id;
$postsViews->ip = \Request::getClientIp();
$postsViews->agent = \Request::header('User-Agent');
$postsViews->save();
}
}
Finally, your method:
public function showpost($titleslug)
{
$post = PostsViews::where('titleslug', '=' ,$titleslug)->firstOrFail();
PostsViews::createViewLog($post);
//Rest of method...
}
To search the most viewed posts in the last 24 hours:
$posts = Posts::join("posts_views", "posts_views.id_post", "=", "posts.id")
->where("created_at", ">=", date("Y-m-d H:i:s", strtotime('-24 hours', time())))
->groupBy("posts.id")
->orderBy(DB::raw('COUNT(posts.id)', 'desc'))
->get(array(DB::raw('COUNT(posts.id) as total_views'), 'posts.*'));
Note that in PostsViews, you have data that can help further filter your listing, such as the session id, in case you do not want to consider hits from the same session.
You may need to adapt some aspects of this solution to your final code.
2020 Update (2)/ With Eloquent Relationships for Laravel 6
If you don't want to add a package to your application. I have developed the following solution based on "Jean Marcos" and "Learner" contribution to the question and my own research.
All credit goes to "Jean Marcos" and "Learner", I felt like I should do the same as Learner and update the code in a way the would be beneficial to others.
First of all, make sure you have a sessions table in the database. Otherwise, follow the steps in Laravel documentations to do so: HTTP Session
Make sure that the sessions are stored in the table. If not, make sure to change the SESSION_DRIVER variable at the .env set to 'database' not 'file' and do composer dump-autoload afterwards.
After that, you are all set to go. You can start by running the following console command:
php artisan make:model PostView -m
This will generate both the model and migration files.
Inside of the migration file put the following Schema. Be cautious with the columns names. For example, my posts table have the "slug" column title name instead of the "titleslug" which was mentioned in the question.
Schema::create('post_views', function (Blueprint $table) {
$table->increments("id");
$table->unsignedInteger("post_id");
$table->string("titleslug");
$table->string("url");
$table->string("session_id");
$table->unsignedInteger('user_id')->nullable();
$table->string("ip");
$table->string("agent");
$table->timestamps();
});
Then put the following code inside the PostView model file.
<?php
namespace App;
use App\Post;
use Illuminate\Database\Eloquent\Model;
class PostView extends Model
{
public function postView()
{
return $this->belongsTo(Post::class);
}
public static function createViewLog($post) {
$postViews= new PostView();
$postViews->post_id = $post->id;
$postViews->slug = $post->slug;
$postViews->url = request()->url();
$postViews->session_id = request()->getSession()->getId();
$postViews->user_id = (auth()->check())?auth()->id():null;
$postViews->ip = request()->ip();
$postViews->agent = request()->header('User-Agent');
$postViews->save();
}
}
Now inside the Post model write the following code. This to create the relation between the posts table and the post_views table.
use App\PostView;
public function postView()
{
return $this->hasMany(PostView::class);
}
In the same Post model you should put the following code. If the user is not logged in the the code will test the IP match. Otherwise, it will test both the session ID and the user ID as each user might have multiple sessions.
public function showPost()
{
if(auth()->id()==null){
return $this->postView()
->where('ip', '=', request()->ip())->exists();
}
return $this->postView()
->where(function($postViewsQuery) { $postViewsQuery
->where('session_id', '=', request()->getSession()->getId())
->orWhere('user_id', '=', (auth()->check()));})->exists();
}
You are ready now to run the migration.
php artisan migrate
When the user ask for the post. The following function should be targeted inside the PostController file:
use App\PostView;
public function show(Post $post)
{
//Some bits from the following query ("category", "user") are made for my own application, but I felt like leaving it for inspiration.
$post = Post::with('category', 'user')->withCount('favorites')->find($post->id);
if($post->showPost()){// this will test if the user viwed the post or not
return $post;
}
$post->increment('views');//I have a separate column for views in the post table. This will increment the views column in the posts table.
PostView::createViewLog($post);
return $post;
}
As I have a separate column for views in the post table. To search the most viewed posts in the last 24 hours you write this code in the controller. Remove paginate if you don't have pagination:
public function mostViwedPosts()
{
return Posts::with('user')->where('created_at','>=', now()->subdays(1))->orderBy('views', 'desc')->latest()->paginate(5);
}
I hope this would help/save someones time.
2020 Update
First of all, thanks a lot "Jean Marcos" for his awesome answer. All credit goes to him, I am just pasting a slightly modified answer combining my knowledge of Laravel as well.
Create a table to save your view records and name it with snake_case plural: post_views
Schema::create("post_views", function(Blueprint $table)
{
$table->engine = "InnoDB";//this is basically optional as you are not using foreign key relationship so you could go with MyISAM as well
$table->increments("id");
//please note to use integer NOT increments as "Jean Marcos' answer" because it will throw error "Incorrect table definition; there can be only one auto column and it must be defined as a key" when running migration.
$table->unsignedInteger("post_id");//note that the Laravel way of defining foreign keys is "table-singular-name_id", so it's preferable to use that
$table->string("titleslug");
$table->string("url");
$table->string("session_id");
$table->unsignedInteger('user_id')->nullable();//here note to make it nullable if your page is accessible publically as well not only by logged in users. Also its more appropriate to have "unsignedInteger" type instead of "string" type as mentioned in Jean Marcos' answer because user_id will save same data as id field of users table which in most cases will be an auto incremented id.
$table->string("ip");
$table->string("agent");
$table->timestamps();
});
Then, create the corresponding model. Please note to create "PascalCase" model name and singular form of the table so it should be like: PostView
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PostView extends Model
{
public static function createViewLog($post) {
$postViews= new PostView();
$postViews->listing_id = $post->id;
$postViews->url = \Request::url();
$postViews->session_id = \Request::getSession()->getId();
$postViews->user_id = (\Auth::check())?\Auth::id():null; //this check will either put the user id or null, no need to use \Auth()->user()->id as we have an inbuild function to get auth id
$postViews->ip = \Request::getClientIp();
$postViews->agent = \Request::header('User-Agent');
$postViews->save();//please note to save it at lease, very important
}
}
Then run the migration to generate this table
php artisan migrate
Finally, your method:
public function showpost($titleslug)
{
$post = PostView::where('titleslug', '=' ,$titleslug)->firstOrFail();
\App\PostView::createViewLog($post);//or add `use App\PostView;` in beginning of the file in order to use only `PostView` here
//Rest of method...
}
To search the most viewed posts in the last 24 hours:
$posts = Posts::join("post_views", "post_views.id_post", "=", "posts.id")
->where("created_at", ">=", date("Y-m-d H:i:s", strtotime('-24 hours', time())))
->groupBy("posts.id")
->orderBy(DB::raw('COUNT(posts.id)'), 'desc')//here its very minute mistake of a paranthesis in Jean Marcos' answer, which results ASC ordering instead of DESC so be careful with this line
->get([DB::raw('COUNT(posts.id) as total_views'), 'posts.*']);
Note that in PostView, you have data that can help further filter your listing, such as the session id, in case you do not want to consider hits from the same session.
You may need to adapt some aspects of this solution to your final code.
So those were few modifications I wanted to point out, also you might want to put an additional column client_internet_ip in which you can store \Request::ip() which can be used as a filter as well if required.
I hope it helps
Eloquent Viewable package can be used for this purpose. It provides more flexible ways to do stuff like this(counting page views).
Note:The Eloquent Viewable package requires PHP 7+ and Laravel 5.5+.
Make Model viewable:
Just add the Viewable trait to the model definition like:
use Illuminate\Database\Eloquent\Model;
use CyrildeWit\EloquentViewable\Viewable;
class Post extends Model
{
use Viewable;
// ...
}
Then in the controller:
public function show(Post $post)
{
$post->addView();
return view('blog.post', compact('post'));
}
After that you can do stuff like this:(see package installation guide for more details)
// Get the total number of views
$post->getViews();
// Get the total number of views since the given date
$post->getViews(Period::since(Carbon::parse('2014-02-23 00:00:00')));
// Get the total number of views between the given date range
$post->getViews(Period::create(Carbon::parse('2014-00-00 00:00:00'), Carbon::parse('2016-00-00 00:00:00')));
// Get the total number of views in the past 6 weeks (from today)
$post->getViews(Period::pastWeeks(6));
// Get the total number of views in the past 2 hours (from now)
$post->getViews(Period::subHours(2));
// Store a new view in the database
$post->addView();
Implements same kind of idea as in the accepted answer, but provides more features and flexibilities.
First of all thanks to user33192 for sharing the eloquent viewable. Just want to make it clearer for others after looking at the docs. Look at the docs to install the package.
Do this in your Post Model:
use Illuminate\Database\Eloquent\Model;
use CyrildeWit\EloquentViewable\InteractsWithViews;
use CyrildeWit\EloquentViewable\Viewable;
class Post extends Model implements Viewable
{
use InteractsWithViews;
// ...
}
In your posts controller, use the record method to save a view;
public function show($slug)
{
$post = Post::where('slug',$slug)->first();
views($post)->record();
return view('posts.show',compact('post'));
}
In your views you can return the views (mine is posts.show) as you want. Check the document for more. I will just the total views of a post.
<button class="btn btn-primary">
{{ views($post)->count() }} <i class="fa fa-eye"></i>
</button>

Laravel - Extending Model

I've created a BaseModel class, which extends from Model. It seemed like everything was working fine, but now I've run into a problem when saving. I'm overriding the save() method in this BaseModel. I'd just like to add some attributes to the model before saving. So I do that, then call return parent::save($options);. The method signature is still the same: public function save(array $options = []).
It appears to be grabbing the name of the BaseModel class for the table name when performing the insert (it's using base_models as the table name), rather than the actual model that is being saved. Has anyone run into this before? What is the proper way of extending from the model class?
I originally created some traits to handle some extra functionality, but thought it would be a better idea to just create a base model and have my models extend from that instead.
In your model (the child one that extends the base model) add the table name explictly for example:
class SomeChildModel extends BaseModel {
// Manually set the table name
protected $table = 'table_name';
}
I realized that I previously had a static method that was creating an instance of itself using new self() and would set a few attributes, back when I was using the methods from a trait. It was fine before, but now since I moved the methods into the base model, that method was actually being called on the base model itself rather than the class that had the trait.
I was basically using the static method to instantiate the class, as I've read it's one way to avoid cluttering the constructor. But I just opted to do it in the constructor this time around since it made sense, so that was my solution.
Laravel will use snake case of the class name by default (the class where save method is called), if no $table instance variable is set. In your case it will use snake case of the BaseModel as a table name. You have two solutions:
Solution 1:
In classes which extends BaseModel add the $table instance variable as follow:
class User extends BaseModel {
protected $table = 'table_name'; // Your table name in the database;
}
Solution 2:
You can use Laravel Eloquent's Events, which allows you to hook into various points in the model's lifecycle.
You can hook into the save method as follow and make your changes. You can use these methods in your BaseClass, in traits, etc. For example in your BaseModel:
class BaseModel extends Model
{
/**
* Listen for save event
*/
protected static function boot()
{
parent::boot();
static::saving(function($model)
{
if ( ! $model->isValid()) {
return false;
}
});
}
}
The above will always call isValid before a model is saved into the storage. In this case it will return false and will not save the object.
For more info see the official docs here. Let me know if it isn't clear.

Laravel 4 Use the same Model and Controller for 12 lists for selects

I have to meke models, controllers and views for 12 tables. They have all the same structure id, name, order.
I was thinking and maybe using:
Controller
index($model)
$model::all()
return View::make(all_tables,compact('model'))
edit($model,$id)... and so on.
But and don't know if there's a way for using only one model.
Did anybody do anything like this?
Any idea?
Thanks
Although each model has the same table structure, what you're trying to achieve would not be advisable as you'd lose a lot of the fluent capabilities of Laravel's Eloquent ORM.
Regarding the controller, this would work:
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class GenericModelController extends Controller
{
public function loadModelById($model, $id)
{
$instance = \App::make('App\\' . ucfirst($model));
return $instance->find($id);
}
}
You'll need the following route:
Route::get('show/{model}/{id}', 'GenericModelController#loadModelById');
Example, to load a user with an id of 1:
http://www.yourdomain.com/show/user/1
Edit: I just saw that you're using Laravel 4, so the syntax for defining a route will be a little different I believe but the general concept will still work. Testing in Laravel 5 and works perfectly.
You should get get some idea from here.Please use the link below.
https://scotch.io/tutorials/a-guide-to-using-eloquent-orm-in-laravel
// app/models/Bear.php
class Bear extends Eloquent {
// MASS ASSIGNMENT -------------------------------------------------------
// define which attributes are mass assignable (for security)
// we only want these 3 attributes able to be filled
protected $fillable = array('name', 'type', 'danger_level');
// DEFINE RELATIONSHIPS --------------------------------------------------
// each bear HAS one fish to eat
public function fish() {
return $this->hasOne('Fish'); // this matches the Eloquent model
}
// each bear climbs many trees
public function trees() {
return $this->hasMany('Tree');
}
// each bear BELONGS to many picnic
// define our pivot table also
public function picnics() {
return $this->belongsToMany('Picnic', 'bears_picnics', 'bear_id', 'picnic_id');
}
}
I find a simple way.
Only one model, one controller and one view(index,edit, etc) too.
A single table with
id, name of list, value (name to appears in the list)
Yo pass can pass to de view all the values per list, and for any list in the table you can create de select if it's no empty.

Ordering table data

I already read this piece from the laravel 5.1 documentation:
$users = DB::table('users')
->orderBy('name', 'desc')
->get();
I have no ideia where to write that.
And this is what I tried to write inside my Model:
class Professor extends Model
{
$professor = DB:table('professor')->orderBy('name','asc')->get();
}
Also tried:
class Professor extends Model
{
Professor::orderBy('name')->get();
//$professor = Professor::orderBy('name')->get();
}
Nothing works e.e
All of them give me erros like:
syntax error, unexpected '$professor' (T_VARIABLE), expecting function (T_FUNCTION)
The piece of code your trying to write should not be placed inside a Model. It should be in a controller or a repository if your using a Repository Pattern.
Assuming you got the following in your code.
A table called professors. A model Professor . A Controller called ProfessorsController.
And a route file with the following code get('professors','ProfessorsController#index');
Then on the controller you should have the following code.
class ProfessorsController extends Controller {
public function index()
{
$professors = \DB:table('professors')->orderBy('name','asc')->get();
return view('proffesors')->with('proffessors',$professors);
}
}
This will return an order list of professors to the view. That is if you are using a view to represent the data.
It does not need to be in model. Most of time something like that goes in controller.
In model you need to define relations or functions that would be used application wide on a object.
If you want to do something similar in model you won't use DB::table you need something like:
class Professor extends \Eloquent
{
public function professorsByName(){
$professionCollection = Professor::all()->orderBy('name','asc')->get();
return $professionCollection;
}
}
Please take it as example it's not something that should go in model at least this simple not.
Mental Note
Never use DB::table reason your observer if any won't execute.
I think you all are missing the primary issue with
class Professor extends Model
{
$professor = DB:table('professor')->orderBy('name','asc')->get();
}
This is not how PHP classes work.
OP, you need to brush up on the concepts of OOP.
What you need is something like this:
your controller:
public function myRoutedMethod()
{
$professors = Professor::getModel()->orderBy('name','asc')->get();
foreach($professors as $professor)
{
var_dump($professor->toArray();
}
}
Or probably even better, create a repository to interface with your model and just call $repository->professors()->toArray();
Google search: Laravel Repository

Resources